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