##// END OF EJS Templates
Backport PR #4209: Magic doc fixes...
Thomas Kluyver -
Show More
@@ -1,325 +1,334 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 =====================
4 4 Cython related magics
5 5 =====================
6 6
7 Magic command interface for interactive work with Cython
8
9 .. note::
10
11 The ``Cython`` package needs to be installed separately. It
12 can be obtained using ``easy_install`` or ``pip``.
13
7 14 Usage
8 15 =====
9 16
17 To enable the magics below, execute ``%load_ext cythonmagic``.
18
10 19 ``%%cython``
11 20
12 21 {CYTHON_DOC}
13 22
14 23 ``%%cython_inline``
15 24
16 25 {CYTHON_INLINE_DOC}
17 26
18 27 ``%%cython_pyximport``
19 28
20 29 {CYTHON_PYXIMPORT_DOC}
21 30
22 31 Author:
23 32 * Brian Granger
24 33
25 34 Parts of this code were taken from Cython.inline.
26 35 """
27 36 #-----------------------------------------------------------------------------
28 37 # Copyright (C) 2010-2011, IPython Development Team.
29 38 #
30 39 # Distributed under the terms of the Modified BSD License.
31 40 #
32 41 # The full license is in the file COPYING.txt, distributed with this software.
33 42 #-----------------------------------------------------------------------------
34 43
35 44 from __future__ import print_function
36 45
37 46 import imp
38 47 import io
39 48 import os
40 49 import re
41 50 import sys
42 51 import time
43 52
44 53 try:
45 54 reload
46 55 except NameError: # Python 3
47 56 from imp import reload
48 57
49 58 try:
50 59 import hashlib
51 60 except ImportError:
52 61 import md5 as hashlib
53 62
54 63 from distutils.core import Distribution, Extension
55 64 from distutils.command.build_ext import build_ext
56 65
57 66 from IPython.core import display
58 67 from IPython.core import magic_arguments
59 68 from IPython.core.magic import Magics, magics_class, cell_magic
60 69 from IPython.utils import py3compat
61 70 from IPython.utils.path import get_ipython_cache_dir
62 71
63 72 import Cython
64 73 from Cython.Compiler.Errors import CompileError
65 74 from Cython.Build.Dependencies import cythonize
66 75
67 76
68 77 @magics_class
69 78 class CythonMagics(Magics):
70 79
71 80 def __init__(self, shell):
72 81 super(CythonMagics,self).__init__(shell)
73 82 self._reloads = {}
74 83 self._code_cache = {}
75 84
76 85 def _import_all(self, module):
77 86 for k,v in module.__dict__.items():
78 87 if not k.startswith('__'):
79 88 self.shell.push({k:v})
80 89
81 90 @cell_magic
82 91 def cython_inline(self, line, cell):
83 92 """Compile and run a Cython code cell using Cython.inline.
84 93
85 94 This magic simply passes the body of the cell to Cython.inline
86 95 and returns the result. If the variables `a` and `b` are defined
87 96 in the user's namespace, here is a simple example that returns
88 97 their sum::
89 98
90 99 %%cython_inline
91 100 return a+b
92 101
93 102 For most purposes, we recommend the usage of the `%%cython` magic.
94 103 """
95 104 locs = self.shell.user_global_ns
96 105 globs = self.shell.user_ns
97 106 return Cython.inline(cell, locals=locs, globals=globs)
98 107
99 108 @cell_magic
100 109 def cython_pyximport(self, line, cell):
101 110 """Compile and import a Cython code cell using pyximport.
102 111
103 112 The contents of the cell are written to a `.pyx` file in the current
104 113 working directory, which is then imported using `pyximport`. This
105 114 magic requires a module name to be passed::
106 115
107 116 %%cython_pyximport modulename
108 117 def f(x):
109 118 return 2.0*x
110 119
111 120 The compiled module is then imported and all of its symbols are
112 121 injected into the user's namespace. For most purposes, we recommend
113 122 the usage of the `%%cython` magic.
114 123 """
115 124 module_name = line.strip()
116 125 if not module_name:
117 126 raise ValueError('module name must be given')
118 127 fname = module_name + '.pyx'
119 128 with io.open(fname, 'w', encoding='utf-8') as f:
120 129 f.write(cell)
121 130 if 'pyximport' not in sys.modules:
122 131 import pyximport
123 132 pyximport.install(reload_support=True)
124 133 if module_name in self._reloads:
125 134 module = self._reloads[module_name]
126 135 reload(module)
127 136 else:
128 137 __import__(module_name)
129 138 module = sys.modules[module_name]
130 139 self._reloads[module_name] = module
131 140 self._import_all(module)
132 141
133 142 @magic_arguments.magic_arguments()
134 143 @magic_arguments.argument(
135 144 '-c', '--compile-args', action='append', default=[],
136 145 help="Extra flags to pass to compiler via the `extra_compile_args` "
137 146 "Extension flag (can be specified multiple times)."
138 147 )
139 148 @magic_arguments.argument(
140 149 '--link-args', action='append', default=[],
141 150 help="Extra flags to pass to linker via the `extra_link_args` "
142 151 "Extension flag (can be specified multiple times)."
143 152 )
144 153 @magic_arguments.argument(
145 154 '-l', '--lib', action='append', default=[],
146 155 help="Add a library to link the extension against (can be specified "
147 156 "multiple times)."
148 157 )
149 158 @magic_arguments.argument(
150 159 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
151 160 help="Add a path to the list of libary directories (can be specified "
152 161 "multiple times)."
153 162 )
154 163 @magic_arguments.argument(
155 164 '-I', '--include', action='append', default=[],
156 165 help="Add a path to the list of include directories (can be specified "
157 166 "multiple times)."
158 167 )
159 168 @magic_arguments.argument(
160 169 '-+', '--cplus', action='store_true', default=False,
161 170 help="Output a C++ rather than C file."
162 171 )
163 172 @magic_arguments.argument(
164 173 '-f', '--force', action='store_true', default=False,
165 174 help="Force the compilation of a new module, even if the source has been "
166 175 "previously compiled."
167 176 )
168 177 @magic_arguments.argument(
169 178 '-a', '--annotate', action='store_true', default=False,
170 179 help="Produce a colorized HTML version of the source."
171 180 )
172 181 @cell_magic
173 182 def cython(self, line, cell):
174 183 """Compile and import everything from a Cython code cell.
175 184
176 185 The contents of the cell are written to a `.pyx` file in the
177 186 directory `IPYTHONDIR/cython` using a filename with the hash of the
178 187 code. This file is then cythonized and compiled. The resulting module
179 188 is imported and all of its symbols are injected into the user's
180 189 namespace. The usage is similar to that of `%%cython_pyximport` but
181 190 you don't have to pass a module name::
182 191
183 192 %%cython
184 193 def f(x):
185 194 return 2.0*x
186 195
187 196 To compile OpenMP codes, pass the required `--compile-args`
188 197 and `--link-args`. For example with gcc::
189 198
190 199 %%cython --compile-args=-fopenmp --link-args=-fopenmp
191 200 ...
192 201 """
193 202 args = magic_arguments.parse_argstring(self.cython, line)
194 203 code = cell if cell.endswith('\n') else cell+'\n'
195 204 lib_dir = os.path.join(get_ipython_cache_dir(), 'cython')
196 205 quiet = True
197 206 key = code, sys.version_info, sys.executable, Cython.__version__
198 207
199 208 if not os.path.exists(lib_dir):
200 209 os.makedirs(lib_dir)
201 210
202 211 if args.force:
203 212 # Force a new module name by adding the current time to the
204 213 # key which is hashed to determine the module name.
205 214 key += time.time(),
206 215
207 216 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
208 217 module_path = os.path.join(lib_dir, module_name + self.so_ext)
209 218
210 219 have_module = os.path.isfile(module_path)
211 220 need_cythonize = not have_module
212 221
213 222 if args.annotate:
214 223 html_file = os.path.join(lib_dir, module_name + '.html')
215 224 if not os.path.isfile(html_file):
216 225 need_cythonize = True
217 226
218 227 if need_cythonize:
219 228 c_include_dirs = args.include
220 229 if 'numpy' in code:
221 230 import numpy
222 231 c_include_dirs.append(numpy.get_include())
223 232 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
224 233 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
225 234 with io.open(pyx_file, 'w', encoding='utf-8') as f:
226 235 f.write(code)
227 236 extension = Extension(
228 237 name = module_name,
229 238 sources = [pyx_file],
230 239 include_dirs = c_include_dirs,
231 240 library_dirs = args.library_dirs,
232 241 extra_compile_args = args.compile_args,
233 242 extra_link_args = args.link_args,
234 243 libraries = args.lib,
235 244 language = 'c++' if args.cplus else 'c',
236 245 )
237 246 build_extension = self._get_build_extension()
238 247 try:
239 248 opts = dict(
240 249 quiet=quiet,
241 250 annotate = args.annotate,
242 251 force = True,
243 252 )
244 253 build_extension.extensions = cythonize([extension], **opts)
245 254 except CompileError:
246 255 return
247 256
248 257 if not have_module:
249 258 build_extension.build_temp = os.path.dirname(pyx_file)
250 259 build_extension.build_lib = lib_dir
251 260 build_extension.run()
252 261 self._code_cache[key] = module_name
253 262
254 263 module = imp.load_dynamic(module_name, module_path)
255 264 self._import_all(module)
256 265
257 266 if args.annotate:
258 267 try:
259 268 with io.open(html_file, encoding='utf-8') as f:
260 269 annotated_html = f.read()
261 270 except IOError as e:
262 271 # File could not be opened. Most likely the user has a version
263 272 # of Cython before 0.15.1 (when `cythonize` learned the
264 273 # `force` keyword argument) and has already compiled this
265 274 # exact source without annotation.
266 275 print('Cython completed successfully but the annotated '
267 276 'source could not be read.', file=sys.stderr)
268 277 print(e, file=sys.stderr)
269 278 else:
270 279 return display.HTML(self.clean_annotated_html(annotated_html))
271 280
272 281 @property
273 282 def so_ext(self):
274 283 """The extension suffix for compiled modules."""
275 284 try:
276 285 return self._so_ext
277 286 except AttributeError:
278 287 self._so_ext = self._get_build_extension().get_ext_filename('')
279 288 return self._so_ext
280 289
281 290 def _clear_distutils_mkpath_cache(self):
282 291 """clear distutils mkpath cache
283 292
284 293 prevents distutils from skipping re-creation of dirs that have been removed
285 294 """
286 295 try:
287 296 from distutils.dir_util import _path_created
288 297 except ImportError:
289 298 pass
290 299 else:
291 300 _path_created.clear()
292 301
293 302 def _get_build_extension(self):
294 303 self._clear_distutils_mkpath_cache()
295 304 dist = Distribution()
296 305 config_files = dist.find_config_files()
297 306 try:
298 307 config_files.remove('setup.cfg')
299 308 except ValueError:
300 309 pass
301 310 dist.parse_config_files(config_files)
302 311 build_extension = build_ext(dist)
303 312 build_extension.finalize_options()
304 313 return build_extension
305 314
306 315 @staticmethod
307 316 def clean_annotated_html(html):
308 317 """Clean up the annotated HTML source.
309 318
310 319 Strips the link to the generated C or C++ file, which we do not
311 320 present to the user.
312 321 """
313 322 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
314 323 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
315 324 return html
316 325
317 326 __doc__ = __doc__.format(
318 327 CYTHON_DOC = ' '*8 + CythonMagics.cython.__doc__,
319 328 CYTHON_INLINE_DOC = ' '*8 + CythonMagics.cython_inline.__doc__,
320 329 CYTHON_PYXIMPORT_DOC = ' '*8 + CythonMagics.cython_pyximport.__doc__,
321 330 )
322 331
323 332 def load_ipython_extension(ip):
324 333 """Load the extension in IPython."""
325 334 ip.register_magics(CythonMagics)
@@ -1,367 +1,371 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 ===========
4 4 octavemagic
5 5 ===========
6 6
7 7 Magics for interacting with Octave via oct2py.
8 8
9 9 .. note::
10 10
11 11 The ``oct2py`` module needs to be installed separately and
12 12 can be obtained using ``easy_install`` or ``pip``.
13 13
14 You will also need a working copy of GNU Octave.
15
14 16 Usage
15 17 =====
16 18
19 To enable the magics below, execute ``%load_ext octavemagic``.
20
17 21 ``%octave``
18 22
19 23 {OCTAVE_DOC}
20 24
21 25 ``%octave_push``
22 26
23 27 {OCTAVE_PUSH_DOC}
24 28
25 29 ``%octave_pull``
26 30
27 31 {OCTAVE_PULL_DOC}
28 32
29 33 """
30 34
31 35 #-----------------------------------------------------------------------------
32 36 # Copyright (C) 2012 The IPython Development Team
33 37 #
34 38 # Distributed under the terms of the BSD License. The full license is in
35 39 # the file COPYING, distributed as part of this software.
36 40 #-----------------------------------------------------------------------------
37 41
38 42 import tempfile
39 43 from glob import glob
40 44 from shutil import rmtree
41 45
42 46 import numpy as np
43 47 import oct2py
44 48 from xml.dom import minidom
45 49
46 50 from IPython.core.displaypub import publish_display_data
47 51 from IPython.core.magic import (Magics, magics_class, line_magic,
48 52 line_cell_magic, needs_local_scope)
49 53 from IPython.testing.skipdoctest import skip_doctest
50 54 from IPython.core.magic_arguments import (
51 55 argument, magic_arguments, parse_argstring
52 56 )
53 57 from IPython.utils.py3compat import unicode_to_str
54 58
55 59 class OctaveMagicError(oct2py.Oct2PyError):
56 60 pass
57 61
58 62 _mimetypes = {'png' : 'image/png',
59 63 'svg' : 'image/svg+xml',
60 64 'jpg' : 'image/jpeg',
61 65 'jpeg': 'image/jpeg'}
62 66
63 67 @magics_class
64 68 class OctaveMagics(Magics):
65 69 """A set of magics useful for interactive work with Octave via oct2py.
66 70 """
67 71 def __init__(self, shell):
68 72 """
69 73 Parameters
70 74 ----------
71 75 shell : IPython shell
72 76
73 77 """
74 78 super(OctaveMagics, self).__init__(shell)
75 79 self._oct = oct2py.Oct2Py()
76 80 self._plot_format = 'png'
77 81
78 82 # Allow publish_display_data to be overridden for
79 83 # testing purposes.
80 84 self._publish_display_data = publish_display_data
81 85
82 86
83 87 def _fix_gnuplot_svg_size(self, image, size=None):
84 88 """
85 89 GnuPlot SVGs do not have height/width attributes. Set
86 90 these to be the same as the viewBox, so that the browser
87 91 scales the image correctly.
88 92
89 93 Parameters
90 94 ----------
91 95 image : str
92 96 SVG data.
93 97 size : tuple of int
94 98 Image width, height.
95 99
96 100 """
97 101 (svg,) = minidom.parseString(image).getElementsByTagName('svg')
98 102 viewbox = svg.getAttribute('viewBox').split(' ')
99 103
100 104 if size is not None:
101 105 width, height = size
102 106 else:
103 107 width, height = viewbox[2:]
104 108
105 109 svg.setAttribute('width', '%dpx' % width)
106 110 svg.setAttribute('height', '%dpx' % height)
107 111 return svg.toxml()
108 112
109 113
110 114 @skip_doctest
111 115 @line_magic
112 116 def octave_push(self, line):
113 117 '''
114 118 Line-level magic that pushes a variable to Octave.
115 119
116 120 `line` should be made up of whitespace separated variable names in the
117 121 IPython namespace::
118 122
119 123 In [7]: import numpy as np
120 124
121 125 In [8]: X = np.arange(5)
122 126
123 127 In [9]: X.mean()
124 128 Out[9]: 2.0
125 129
126 130 In [10]: %octave_push X
127 131
128 132 In [11]: %octave mean(X)
129 133 Out[11]: 2.0
130 134
131 135 '''
132 136 inputs = line.split(' ')
133 137 for input in inputs:
134 138 input = unicode_to_str(input)
135 139 self._oct.put(input, self.shell.user_ns[input])
136 140
137 141
138 142 @skip_doctest
139 143 @line_magic
140 144 def octave_pull(self, line):
141 145 '''
142 146 Line-level magic that pulls a variable from Octave.
143 147
144 148 In [18]: _ = %octave x = [1 2; 3 4]; y = 'hello'
145 149
146 150 In [19]: %octave_pull x y
147 151
148 152 In [20]: x
149 153 Out[20]:
150 154 array([[ 1., 2.],
151 155 [ 3., 4.]])
152 156
153 157 In [21]: y
154 158 Out[21]: 'hello'
155 159
156 160 '''
157 161 outputs = line.split(' ')
158 162 for output in outputs:
159 163 output = unicode_to_str(output)
160 164 self.shell.push({output: self._oct.get(output)})
161 165
162 166
163 167 @skip_doctest
164 168 @magic_arguments()
165 169 @argument(
166 170 '-i', '--input', action='append',
167 171 help='Names of input variables to be pushed to Octave. Multiple names '
168 172 'can be passed, separated by commas with no whitespace.'
169 173 )
170 174 @argument(
171 175 '-o', '--output', action='append',
172 176 help='Names of variables to be pulled from Octave after executing cell '
173 177 'body. Multiple names can be passed, separated by commas with no '
174 178 'whitespace.'
175 179 )
176 180 @argument(
177 181 '-s', '--size', action='store',
178 182 help='Pixel size of plots, "width,height". Default is "-s 400,250".'
179 183 )
180 184 @argument(
181 185 '-f', '--format', action='store',
182 186 help='Plot format (png, svg or jpg).'
183 187 )
184 188
185 189 @needs_local_scope
186 190 @argument(
187 191 'code',
188 192 nargs='*',
189 193 )
190 194 @line_cell_magic
191 195 def octave(self, line, cell=None, local_ns=None):
192 196 '''
193 197 Execute code in Octave, and pull some of the results back into the
194 198 Python namespace.
195 199
196 200 In [9]: %octave X = [1 2; 3 4]; mean(X)
197 201 Out[9]: array([[ 2., 3.]])
198 202
199 203 As a cell, this will run a block of Octave code, without returning any
200 204 value::
201 205
202 206 In [10]: %%octave
203 207 ....: p = [-2, -1, 0, 1, 2]
204 208 ....: polyout(p, 'x')
205 209
206 210 -2*x^4 - 1*x^3 + 0*x^2 + 1*x^1 + 2
207 211
208 212 In the notebook, plots are published as the output of the cell, e.g.
209 213
210 214 %octave plot([1 2 3], [4 5 6])
211 215
212 216 will create a line plot.
213 217
214 218 Objects can be passed back and forth between Octave and IPython via the
215 219 -i and -o flags in line::
216 220
217 221 In [14]: Z = np.array([1, 4, 5, 10])
218 222
219 223 In [15]: %octave -i Z mean(Z)
220 224 Out[15]: array([ 5.])
221 225
222 226
223 227 In [16]: %octave -o W W = Z * mean(Z)
224 228 Out[16]: array([ 5., 20., 25., 50.])
225 229
226 230 In [17]: W
227 231 Out[17]: array([ 5., 20., 25., 50.])
228 232
229 233 The size and format of output plots can be specified::
230 234
231 235 In [18]: %%octave -s 600,800 -f svg
232 236 ...: plot([1, 2, 3]);
233 237
234 238 '''
235 239 args = parse_argstring(self.octave, line)
236 240
237 241 # arguments 'code' in line are prepended to the cell lines
238 242 if cell is None:
239 243 code = ''
240 244 return_output = True
241 245 else:
242 246 code = cell
243 247 return_output = False
244 248
245 249 code = ' '.join(args.code) + code
246 250
247 251 # if there is no local namespace then default to an empty dict
248 252 if local_ns is None:
249 253 local_ns = {}
250 254
251 255 if args.input:
252 256 for input in ','.join(args.input).split(','):
253 257 input = unicode_to_str(input)
254 258 try:
255 259 val = local_ns[input]
256 260 except KeyError:
257 261 val = self.shell.user_ns[input]
258 262 self._oct.put(input, val)
259 263
260 264 # generate plots in a temporary directory
261 265 plot_dir = tempfile.mkdtemp().replace('\\', '/')
262 266 if args.size is not None:
263 267 size = args.size
264 268 else:
265 269 size = '400,240'
266 270
267 271 if args.format is not None:
268 272 plot_format = args.format
269 273 else:
270 274 plot_format = 'png'
271 275
272 276 pre_call = '''
273 277 global __ipy_figures = [];
274 278 page_screen_output(0);
275 279
276 280 function fig_create(src, event)
277 281 global __ipy_figures;
278 282 __ipy_figures(size(__ipy_figures) + 1) = src;
279 283 set(src, "visible", "off");
280 284 end
281 285
282 286 set(0, 'DefaultFigureCreateFcn', @fig_create);
283 287
284 288 close all;
285 289 clear ans;
286 290
287 291 # ___<end_pre_call>___ #
288 292 '''
289 293
290 294 post_call = '''
291 295 # ___<start_post_call>___ #
292 296
293 297 # Save output of the last execution
294 298 if exist("ans") == 1
295 299 _ = ans;
296 300 else
297 301 _ = nan;
298 302 end
299 303
300 304 for f = __ipy_figures
301 305 outfile = sprintf('%(plot_dir)s/__ipy_oct_fig_%%03d.png', f);
302 306 try
303 307 print(f, outfile, '-d%(plot_format)s', '-tight', '-S%(size)s');
304 308 end
305 309 end
306 310
307 311 ''' % locals()
308 312
309 313 code = ' '.join((pre_call, code, post_call))
310 314 try:
311 315 text_output = self._oct.run(code, verbose=False)
312 316 except (oct2py.Oct2PyError) as exception:
313 317 msg = exception.message
314 318 msg = msg.split('# ___<end_pre_call>___ #')[1]
315 319 msg = msg.split('# ___<start_post_call>___ #')[0]
316 320 raise OctaveMagicError('Octave could not complete execution. '
317 321 'Traceback (currently broken in oct2py): %s'
318 322 % msg)
319 323
320 324 key = 'OctaveMagic.Octave'
321 325 display_data = []
322 326
323 327 # Publish text output
324 328 if text_output:
325 329 display_data.append((key, {'text/plain': text_output}))
326 330
327 331 # Publish images
328 332 images = [open(imgfile, 'rb').read() for imgfile in \
329 333 glob("%s/*" % plot_dir)]
330 334 rmtree(plot_dir)
331 335
332 336 plot_mime_type = _mimetypes.get(plot_format, 'image/png')
333 337 width, height = [int(s) for s in size.split(',')]
334 338 for image in images:
335 339 if plot_format == 'svg':
336 340 image = self._fix_gnuplot_svg_size(image, size=(width, height))
337 341 display_data.append((key, {plot_mime_type: image}))
338 342
339 343 if args.output:
340 344 for output in ','.join(args.output).split(','):
341 345 output = unicode_to_str(output)
342 346 self.shell.push({output: self._oct.get(output)})
343 347
344 348 for source, data in display_data:
345 349 self._publish_display_data(source, data)
346 350
347 351 if return_output:
348 352 ans = self._oct.get('_')
349 353
350 354 # Unfortunately, Octave doesn't have a "None" object,
351 355 # so we can't return any NaN outputs
352 356 if np.isscalar(ans) and np.isnan(ans):
353 357 ans = None
354 358
355 359 return ans
356 360
357 361
358 362 __doc__ = __doc__.format(
359 363 OCTAVE_DOC = ' '*8 + OctaveMagics.octave.__doc__,
360 364 OCTAVE_PUSH_DOC = ' '*8 + OctaveMagics.octave_push.__doc__,
361 365 OCTAVE_PULL_DOC = ' '*8 + OctaveMagics.octave_pull.__doc__
362 366 )
363 367
364 368
365 369 def load_ipython_extension(ip):
366 370 """Load the extension in IPython."""
367 371 ip.register_magics(OctaveMagics)
@@ -1,667 +1,676 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 ======
4 4 Rmagic
5 5 ======
6 6
7 7 Magic command interface for interactive work with R via rpy2
8 8
9 .. note::
10
11 The ``rpy2`` package needs to be installed separately. It
12 can be obtained using ``easy_install`` or ``pip``.
13
14 You will also need a working copy of R.
15
9 16 Usage
10 17 =====
11 18
19 To enable the magics below, execute ``%load_ext rmagic``.
20
12 21 ``%R``
13 22
14 23 {R_DOC}
15 24
16 25 ``%Rpush``
17 26
18 27 {RPUSH_DOC}
19 28
20 29 ``%Rpull``
21 30
22 31 {RPULL_DOC}
23 32
24 33 ``%Rget``
25 34
26 35 {RGET_DOC}
27 36
28 37 """
29 38
30 39 #-----------------------------------------------------------------------------
31 40 # Copyright (C) 2012 The IPython Development Team
32 41 #
33 42 # Distributed under the terms of the BSD License. The full license is in
34 43 # the file COPYING, distributed as part of this software.
35 44 #-----------------------------------------------------------------------------
36 45
37 46 import sys
38 47 import tempfile
39 48 from glob import glob
40 49 from shutil import rmtree
41 50
42 51 # numpy and rpy2 imports
43 52
44 53 import numpy as np
45 54
46 55 import rpy2.rinterface as ri
47 56 import rpy2.robjects as ro
48 57 try:
49 58 from rpy2.robjects import pandas2ri
50 59 pandas2ri.activate()
51 60 except ImportError:
52 61 pandas2ri = None
53 62 from rpy2.robjects import numpy2ri
54 63 numpy2ri.activate()
55 64
56 65 # IPython imports
57 66
58 67 from IPython.core.displaypub import publish_display_data
59 68 from IPython.core.magic import (Magics, magics_class, line_magic,
60 69 line_cell_magic, needs_local_scope)
61 70 from IPython.testing.skipdoctest import skip_doctest
62 71 from IPython.core.magic_arguments import (
63 72 argument, magic_arguments, parse_argstring
64 73 )
65 74 from IPython.external.simplegeneric import generic
66 75 from IPython.utils.py3compat import str_to_unicode, unicode_to_str, PY3
67 76
68 77 class RInterpreterError(ri.RRuntimeError):
69 78 """An error when running R code in a %%R magic cell."""
70 79 def __init__(self, line, err, stdout):
71 80 self.line = line
72 81 self.err = err.rstrip()
73 82 self.stdout = stdout.rstrip()
74 83
75 84 def __unicode__(self):
76 85 s = 'Failed to parse and evaluate line %r.\nR error message: %r' % \
77 86 (self.line, self.err)
78 87 if self.stdout and (self.stdout != self.err):
79 88 s += '\nR stdout:\n' + self.stdout
80 89 return s
81 90
82 91 if PY3:
83 92 __str__ = __unicode__
84 93 else:
85 94 def __str__(self):
86 95 return unicode_to_str(unicode(self), 'utf-8')
87 96
88 97 def Rconverter(Robj, dataframe=False):
89 98 """
90 99 Convert an object in R's namespace to one suitable
91 100 for ipython's namespace.
92 101
93 102 For a data.frame, it tries to return a structured array.
94 103 It first checks for colnames, then names.
95 104 If all are NULL, it returns np.asarray(Robj), else
96 105 it tries to construct a recarray
97 106
98 107 Parameters
99 108 ----------
100 109
101 110 Robj: an R object returned from rpy2
102 111 """
103 112 is_data_frame = ro.r('is.data.frame')
104 113 colnames = ro.r('colnames')
105 114 rownames = ro.r('rownames') # with pandas, these could be used for the index
106 115 names = ro.r('names')
107 116
108 117 if dataframe:
109 118 as_data_frame = ro.r('as.data.frame')
110 119 cols = colnames(Robj)
111 120 _names = names(Robj)
112 121 if cols != ri.NULL:
113 122 Robj = as_data_frame(Robj)
114 123 names = tuple(np.array(cols))
115 124 elif _names != ri.NULL:
116 125 names = tuple(np.array(_names))
117 126 else: # failed to find names
118 127 return np.asarray(Robj)
119 128 Robj = np.rec.fromarrays(Robj, names = names)
120 129 return np.asarray(Robj)
121 130
122 131 @generic
123 132 def pyconverter(pyobj):
124 133 """Convert Python objects to R objects. Add types using the decorator:
125 134
126 135 @pyconverter.when_type
127 136 """
128 137 return pyobj
129 138
130 139 # The default conversion for lists seems to make them a nested list. That has
131 140 # some advantages, but is rarely convenient, so for interactive use, we convert
132 141 # lists to a numpy array, which becomes an R vector.
133 142 @pyconverter.when_type(list)
134 143 def pyconverter_list(pyobj):
135 144 return np.asarray(pyobj)
136 145
137 146 if pandas2ri is None:
138 147 # pandas2ri was new in rpy2 2.3.3, so for now we'll fallback to pandas'
139 148 # conversion function.
140 149 try:
141 150 from pandas import DataFrame
142 151 from pandas.rpy.common import convert_to_r_dataframe
143 152 @pyconverter.when_type(DataFrame)
144 153 def pyconverter_dataframe(pyobj):
145 154 return convert_to_r_dataframe(pyobj, strings_as_factors=True)
146 155 except ImportError:
147 156 pass
148 157
149 158 @magics_class
150 159 class RMagics(Magics):
151 160 """A set of magics useful for interactive work with R via rpy2.
152 161 """
153 162
154 163 def __init__(self, shell, Rconverter=Rconverter,
155 164 pyconverter=pyconverter,
156 165 cache_display_data=False):
157 166 """
158 167 Parameters
159 168 ----------
160 169
161 170 shell : IPython shell
162 171
163 172 Rconverter : callable
164 173 To be called on values taken from R before putting them in the
165 174 IPython namespace.
166 175
167 176 pyconverter : callable
168 177 To be called on values in ipython namespace before
169 178 assigning to variables in rpy2.
170 179
171 180 cache_display_data : bool
172 181 If True, the published results of the final call to R are
173 182 cached in the variable 'display_cache'.
174 183
175 184 """
176 185 super(RMagics, self).__init__(shell)
177 186 self.cache_display_data = cache_display_data
178 187
179 188 self.r = ro.R()
180 189
181 190 self.Rstdout_cache = []
182 191 self.pyconverter = pyconverter
183 192 self.Rconverter = Rconverter
184 193
185 194 def eval(self, line):
186 195 '''
187 196 Parse and evaluate a line with rpy2.
188 197 Returns the output to R's stdout() connection
189 198 and the value of eval(parse(line)).
190 199 '''
191 200 old_writeconsole = ri.get_writeconsole()
192 201 ri.set_writeconsole(self.write_console)
193 202 try:
194 203 value = ri.baseenv['eval'](ri.parse(line))
195 204 except (ri.RRuntimeError, ValueError) as exception:
196 205 warning_or_other_msg = self.flush() # otherwise next return seems to have copy of error
197 206 raise RInterpreterError(line, str_to_unicode(str(exception)), warning_or_other_msg)
198 207 text_output = self.flush()
199 208 ri.set_writeconsole(old_writeconsole)
200 209 return text_output, value
201 210
202 211 def write_console(self, output):
203 212 '''
204 213 A hook to capture R's stdout in a cache.
205 214 '''
206 215 self.Rstdout_cache.append(output)
207 216
208 217 def flush(self):
209 218 '''
210 219 Flush R's stdout cache to a string, returning the string.
211 220 '''
212 221 value = ''.join([str_to_unicode(s, 'utf-8') for s in self.Rstdout_cache])
213 222 self.Rstdout_cache = []
214 223 return value
215 224
216 225 @skip_doctest
217 226 @needs_local_scope
218 227 @line_magic
219 228 def Rpush(self, line, local_ns=None):
220 229 '''
221 230 A line-level magic for R that pushes
222 231 variables from python to rpy2. The line should be made up
223 232 of whitespace separated variable names in the IPython
224 233 namespace::
225 234
226 235 In [7]: import numpy as np
227 236
228 237 In [8]: X = np.array([4.5,6.3,7.9])
229 238
230 239 In [9]: X.mean()
231 240 Out[9]: 6.2333333333333343
232 241
233 242 In [10]: %Rpush X
234 243
235 244 In [11]: %R mean(X)
236 245 Out[11]: array([ 6.23333333])
237 246
238 247 '''
239 248 if local_ns is None:
240 249 local_ns = {}
241 250
242 251 inputs = line.split(' ')
243 252 for input in inputs:
244 253 try:
245 254 val = local_ns[input]
246 255 except KeyError:
247 256 try:
248 257 val = self.shell.user_ns[input]
249 258 except KeyError:
250 259 # reraise the KeyError as a NameError so that it looks like
251 260 # the standard python behavior when you use an unnamed
252 261 # variable
253 262 raise NameError("name '%s' is not defined" % input)
254 263
255 264 self.r.assign(input, self.pyconverter(val))
256 265
257 266 @skip_doctest
258 267 @magic_arguments()
259 268 @argument(
260 269 '-d', '--as_dataframe', action='store_true',
261 270 default=False,
262 271 help='Convert objects to data.frames before returning to ipython.'
263 272 )
264 273 @argument(
265 274 'outputs',
266 275 nargs='*',
267 276 )
268 277 @line_magic
269 278 def Rpull(self, line):
270 279 '''
271 280 A line-level magic for R that pulls
272 281 variables from python to rpy2::
273 282
274 283 In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4)
275 284
276 285 In [19]: %Rpull x y z
277 286
278 287 In [20]: x
279 288 Out[20]: array([ 3. , 4. , 6.7])
280 289
281 290 In [21]: y
282 291 Out[21]: array([ 4., 6., 7.])
283 292
284 293 In [22]: z
285 294 Out[22]:
286 295 array(['a', '3', '4'],
287 296 dtype='|S1')
288 297
289 298
290 299 If --as_dataframe, then each object is returned as a structured array
291 300 after first passed through "as.data.frame" in R before
292 301 being calling self.Rconverter.
293 302 This is useful when a structured array is desired as output, or
294 303 when the object in R has mixed data types.
295 304 See the %%R docstring for more examples.
296 305
297 306 Notes
298 307 -----
299 308
300 309 Beware that R names can have '.' so this is not fool proof.
301 310 To avoid this, don't name your R objects with '.'s...
302 311
303 312 '''
304 313 args = parse_argstring(self.Rpull, line)
305 314 outputs = args.outputs
306 315 for output in outputs:
307 316 self.shell.push({output:self.Rconverter(self.r(output),dataframe=args.as_dataframe)})
308 317
309 318 @skip_doctest
310 319 @magic_arguments()
311 320 @argument(
312 321 '-d', '--as_dataframe', action='store_true',
313 322 default=False,
314 323 help='Convert objects to data.frames before returning to ipython.'
315 324 )
316 325 @argument(
317 326 'output',
318 327 nargs=1,
319 328 type=str,
320 329 )
321 330 @line_magic
322 331 def Rget(self, line):
323 332 '''
324 333 Return an object from rpy2, possibly as a structured array (if possible).
325 334 Similar to Rpull except only one argument is accepted and the value is
326 335 returned rather than pushed to self.shell.user_ns::
327 336
328 337 In [3]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
329 338
330 339 In [4]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
331 340
332 341 In [5]: %R -i datapy
333 342
334 343 In [6]: %Rget datapy
335 344 Out[6]:
336 345 array([['1', '2', '3', '4'],
337 346 ['2', '3', '2', '5'],
338 347 ['a', 'b', 'c', 'e']],
339 348 dtype='|S1')
340 349
341 350 In [7]: %Rget -d datapy
342 351 Out[7]:
343 352 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
344 353 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
345 354
346 355 '''
347 356 args = parse_argstring(self.Rget, line)
348 357 output = args.output
349 358 return self.Rconverter(self.r(output[0]),dataframe=args.as_dataframe)
350 359
351 360
352 361 @skip_doctest
353 362 @magic_arguments()
354 363 @argument(
355 364 '-i', '--input', action='append',
356 365 help='Names of input variable from shell.user_ns to be assigned to R variables of the same names after calling self.pyconverter. Multiple names can be passed separated only by commas with no whitespace.'
357 366 )
358 367 @argument(
359 368 '-o', '--output', action='append',
360 369 help='Names of variables to be pushed from rpy2 to shell.user_ns after executing cell body and applying self.Rconverter. Multiple names can be passed separated only by commas with no whitespace.'
361 370 )
362 371 @argument(
363 372 '-w', '--width', type=int,
364 373 help='Width of png plotting device sent as an argument to *png* in R.'
365 374 )
366 375 @argument(
367 376 '-h', '--height', type=int,
368 377 help='Height of png plotting device sent as an argument to *png* in R.'
369 378 )
370 379
371 380 @argument(
372 381 '-d', '--dataframe', action='append',
373 382 help='Convert these objects to data.frames and return as structured arrays.'
374 383 )
375 384 @argument(
376 385 '-u', '--units', type=unicode, choices=["px", "in", "cm", "mm"],
377 386 help='Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].'
378 387 )
379 388 @argument(
380 389 '-r', '--res', type=int,
381 390 help='Resolution of png plotting device sent as an argument to *png* in R. Defaults to 72 if *units* is one of ["in", "cm", "mm"].'
382 391 )
383 392 @argument(
384 393 '-p', '--pointsize', type=int,
385 394 help='Pointsize of png plotting device sent as an argument to *png* in R.'
386 395 )
387 396 @argument(
388 397 '-b', '--bg',
389 398 help='Background of png plotting device sent as an argument to *png* in R.'
390 399 )
391 400 @argument(
392 401 '-n', '--noreturn',
393 402 help='Force the magic to not return anything.',
394 403 action='store_true',
395 404 default=False
396 405 )
397 406 @argument(
398 407 'code',
399 408 nargs='*',
400 409 )
401 410 @needs_local_scope
402 411 @line_cell_magic
403 412 def R(self, line, cell=None, local_ns=None):
404 413 '''
405 414 Execute code in R, and pull some of the results back into the Python namespace.
406 415
407 416 In line mode, this will evaluate an expression and convert the returned value to a Python object.
408 417 The return value is determined by rpy2's behaviour of returning the result of evaluating the
409 418 final line.
410 419
411 420 Multiple R lines can be executed by joining them with semicolons::
412 421
413 422 In [9]: %R X=c(1,4,5,7); sd(X); mean(X)
414 423 Out[9]: array([ 4.25])
415 424
416 425 As a cell, this will run a block of R code, without bringing anything back by default::
417 426
418 427 In [10]: %%R
419 428 ....: Y = c(2,4,3,9)
420 429 ....: print(summary(lm(Y~X)))
421 430 ....:
422 431
423 432 Call:
424 433 lm(formula = Y ~ X)
425 434
426 435 Residuals:
427 436 1 2 3 4
428 437 0.88 -0.24 -2.28 1.64
429 438
430 439 Coefficients:
431 440 Estimate Std. Error t value Pr(>|t|)
432 441 (Intercept) 0.0800 2.3000 0.035 0.975
433 442 X 1.0400 0.4822 2.157 0.164
434 443
435 444 Residual standard error: 2.088 on 2 degrees of freedom
436 445 Multiple R-squared: 0.6993,Adjusted R-squared: 0.549
437 446 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638
438 447
439 448 In the notebook, plots are published as the output of the cell.
440 449
441 450 %R plot(X, Y)
442 451
443 452 will create a scatter plot of X bs Y.
444 453
445 454 If cell is not None and line has some R code, it is prepended to
446 455 the R code in cell.
447 456
448 457 Objects can be passed back and forth between rpy2 and python via the -i -o flags in line::
449 458
450 459 In [14]: Z = np.array([1,4,5,10])
451 460
452 461 In [15]: %R -i Z mean(Z)
453 462 Out[15]: array([ 5.])
454 463
455 464
456 465 In [16]: %R -o W W=Z*mean(Z)
457 466 Out[16]: array([ 5., 20., 25., 50.])
458 467
459 468 In [17]: W
460 469 Out[17]: array([ 5., 20., 25., 50.])
461 470
462 471 The return value is determined by these rules:
463 472
464 473 * If the cell is not None, the magic returns None.
465 474
466 475 * If the cell evaluates as False, the resulting value is returned
467 unless the final line prints something to the console, in
468 which case None is returned.
476 unless the final line prints something to the console, in
477 which case None is returned.
469 478
470 479 * If the final line results in a NULL value when evaluated
471 by rpy2, then None is returned.
480 by rpy2, then None is returned.
472 481
473 482 * No attempt is made to convert the final value to a structured array.
474 Use the --dataframe flag or %Rget to push / return a structured array.
483 Use the --dataframe flag or %Rget to push / return a structured array.
475 484
476 485 * If the -n flag is present, there is no return value.
477 486
478 487 * A trailing ';' will also result in no return value as the last
479 value in the line is an empty string.
488 value in the line is an empty string.
480 489
481 490 The --dataframe argument will attempt to return structured arrays.
482 491 This is useful for dataframes with
483 492 mixed data types. Note also that for a data.frame,
484 493 if it is returned as an ndarray, it is transposed::
485 494
486 495 In [18]: dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')]
487 496
488 497 In [19]: datapy = np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5, 'e')], dtype=dtype)
489 498
490 499 In [20]: %%R -o datar
491 500 datar = datapy
492 501 ....:
493 502
494 503 In [21]: datar
495 504 Out[21]:
496 505 array([['1', '2', '3', '4'],
497 506 ['2', '3', '2', '5'],
498 507 ['a', 'b', 'c', 'e']],
499 508 dtype='|S1')
500 509
501 510 In [22]: %%R -d datar
502 511 datar = datapy
503 512 ....:
504 513
505 514 In [23]: datar
506 515 Out[23]:
507 516 array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c'), (4, 5.0, 'e')],
508 517 dtype=[('x', '<i4'), ('y', '<f8'), ('z', '|S1')])
509 518
510 519 The --dataframe argument first tries colnames, then names.
511 520 If both are NULL, it returns an ndarray (i.e. unstructured)::
512 521
513 522 In [1]: %R mydata=c(4,6,8.3); NULL
514 523
515 524 In [2]: %R -d mydata
516 525
517 526 In [3]: mydata
518 527 Out[3]: array([ 4. , 6. , 8.3])
519 528
520 529 In [4]: %R names(mydata) = c('a','b','c'); NULL
521 530
522 531 In [5]: %R -d mydata
523 532
524 533 In [6]: mydata
525 534 Out[6]:
526 535 array((4.0, 6.0, 8.3),
527 536 dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])
528 537
529 538 In [7]: %R -o mydata
530 539
531 540 In [8]: mydata
532 541 Out[8]: array([ 4. , 6. , 8.3])
533 542
534 543 '''
535 544
536 545 args = parse_argstring(self.R, line)
537 546
538 547 # arguments 'code' in line are prepended to
539 548 # the cell lines
540 549
541 550 if cell is None:
542 551 code = ''
543 552 return_output = True
544 553 line_mode = True
545 554 else:
546 555 code = cell
547 556 return_output = False
548 557 line_mode = False
549 558
550 559 code = ' '.join(args.code) + code
551 560
552 561 # if there is no local namespace then default to an empty dict
553 562 if local_ns is None:
554 563 local_ns = {}
555 564
556 565 if args.input:
557 566 for input in ','.join(args.input).split(','):
558 567 try:
559 568 val = local_ns[input]
560 569 except KeyError:
561 570 try:
562 571 val = self.shell.user_ns[input]
563 572 except KeyError:
564 573 raise NameError("name '%s' is not defined" % input)
565 574 self.r.assign(input, self.pyconverter(val))
566 575
567 576 if getattr(args, 'units') is not None:
568 577 if args.units != "px" and getattr(args, 'res') is None:
569 578 args.res = 72
570 579 args.units = '"%s"' % args.units
571 580
572 581 png_argdict = dict([(n, getattr(args, n)) for n in ['units', 'res', 'height', 'width', 'bg', 'pointsize']])
573 582 png_args = ','.join(['%s=%s' % (o,v) for o, v in png_argdict.items() if v is not None])
574 583 # execute the R code in a temporary directory
575 584
576 585 tmpd = tempfile.mkdtemp()
577 586 self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd.replace('\\', '/'), png_args))
578 587
579 588 text_output = ''
580 589 try:
581 590 if line_mode:
582 591 for line in code.split(';'):
583 592 text_result, result = self.eval(line)
584 593 text_output += text_result
585 594 if text_result:
586 595 # the last line printed something to the console so we won't return it
587 596 return_output = False
588 597 else:
589 598 text_result, result = self.eval(code)
590 599 text_output += text_result
591 600
592 601 except RInterpreterError as e:
593 602 print(e.stdout)
594 603 if not e.stdout.endswith(e.err):
595 604 print(e.err)
596 605 rmtree(tmpd)
597 606 return
598 607
599 608 self.r('dev.off()')
600 609
601 610 # read out all the saved .png files
602 611
603 612 images = [open(imgfile, 'rb').read() for imgfile in glob("%s/Rplots*png" % tmpd)]
604 613
605 614 # now publish the images
606 615 # mimicking IPython/zmq/pylab/backend_inline.py
607 616 fmt = 'png'
608 617 mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
609 618 mime = mimetypes[fmt]
610 619
611 620 # publish the printed R objects, if any
612 621
613 622 display_data = []
614 623 if text_output:
615 624 display_data.append(('RMagic.R', {'text/plain':text_output}))
616 625
617 626 # flush text streams before sending figures, helps a little with output
618 627 for image in images:
619 628 # synchronization in the console (though it's a bandaid, not a real sln)
620 629 sys.stdout.flush(); sys.stderr.flush()
621 630 display_data.append(('RMagic.R', {mime: image}))
622 631
623 632 # kill the temporary directory
624 633 rmtree(tmpd)
625 634
626 635 # try to turn every output into a numpy array
627 636 # this means that output are assumed to be castable
628 637 # as numpy arrays
629 638
630 639 if args.output:
631 640 for output in ','.join(args.output).split(','):
632 641 self.shell.push({output:self.Rconverter(self.r(output), dataframe=False)})
633 642
634 643 if args.dataframe:
635 644 for output in ','.join(args.dataframe).split(','):
636 645 self.shell.push({output:self.Rconverter(self.r(output), dataframe=True)})
637 646
638 647 for tag, disp_d in display_data:
639 648 publish_display_data(tag, disp_d)
640 649
641 650 # this will keep a reference to the display_data
642 651 # which might be useful to other objects who happen to use
643 652 # this method
644 653
645 654 if self.cache_display_data:
646 655 self.display_cache = display_data
647 656
648 657 # if in line mode and return_output, return the result as an ndarray
649 658 if return_output and not args.noreturn:
650 659 if result != ri.NULL:
651 660 return self.Rconverter(result, dataframe=False)
652 661
653 662 __doc__ = __doc__.format(
654 663 R_DOC = ' '*8 + RMagics.R.__doc__,
655 664 RPUSH_DOC = ' '*8 + RMagics.Rpush.__doc__,
656 665 RPULL_DOC = ' '*8 + RMagics.Rpull.__doc__,
657 666 RGET_DOC = ' '*8 + RMagics.Rget.__doc__
658 667 )
659 668
660 669
661 670 def load_ipython_extension(ip):
662 671 """Load the extension in IPython."""
663 672 ip.register_magics(RMagics)
664 673 # Initialising rpy2 interferes with readline. Since, at this point, we've
665 674 # probably just loaded rpy2, we reset the delimiters. See issue gh-2759.
666 675 if ip.has_readline:
667 676 ip.readline.set_completer_delims(ip.readline_delims)
General Comments 0
You need to be logged in to leave comments. Login now