forked from jonathan-taylor/Rmagic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Rmagic.py
132 lines (101 loc) · 4.35 KB
/
Rmagic.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import rpy2.rinterface as ri
import numpy as np
import rpy2.robjects as ro
import sys
import tempfile
from glob import glob
from shutil import rmtree
from getopt import getopt
# for publishing the information to the frontends
from IPython.core.displaypub import publish_display_data
# enable conversion of numpy arrays into R objects (following to rpy2 documentation)
from rpy2.robjects.numpy2ri import numpy2ri
ro.conversion.py2ri = numpy2ri
class Rmagic(object):
def __init__(self, shell=None):
self.r = ro.R()
self.output = []
self.eval = ri.baseenv['eval']
self.shell = shell
def write_console(self, output):
self.output.append(output)
def flush(self):
value = ''.join(self.output)
self.output = []
return value
def cell_magic(self, args, text):
# need to get the ipython instance for assigning
opts, args = getopt(args.strip().split(' '), None, ['inputs=',
'outputs=',
# these are options for png
'width=',
'height=',
'units=',
'pointsize=',
'bg='])
opts = dict(opts)
outputs = []
for option, value in opts.items():
if option == '--inputs':
# need to have access the shell to assign these
# python variables to variables in R
opts.pop('--inputs')
# with self.shell, we will assign the values to variables in the shell
# for now, this is a hack, with self.shell a dictionary
for input in value.split(','):
self.r.assign(input, self.shell[input])
if option == '--outputs':
outputs = value.split(',')
opts.pop('--outputs')
png_args = ','.join(['%s=%s' % (o[2:],v) for o, v in opts.items()])
# execute the R code in a temporary directory
tmpd = tempfile.mkdtemp()
self.r('png("%s/Rplots%%03d.png",%s)' % (tmpd, png_args))
self.eval(ri.parse(text))
self.r('dev.off()')
# read out all the saved .png files
images = [file(imgfile).read() for imgfile in glob("%s/Rplots*png" % tmpd)]
# now publish the images
# mimicking IPython/zmq/pylab/backend_inline.py
fmt = 'png'
mimetypes = { 'png' : 'image/png', 'svg' : 'image/svg+xml' }
mime = mimetypes[fmt]
# publish the printed R objects, if any
publish_display_data('Rmagic.cell_magic', {'text/plain':self.flush()})
# flush text streams before sending figures, helps a little with output
for image in images:
# synchronization in the console (though it's a bandaid, not a real sln)
sys.stdout.flush(); sys.stderr.flush()
publish_display_data(
'Rmagic.cell_magic',
{mime : image}
)
value = {}
# try to turn every output into a numpy array
# this means that outputs are assumed to be castable
# as numpy arrays
for output in outputs:
# with self.shell, we will assign the values to variables in the shell
self.shell[output] = np.asarray(self.r(output))
# kill the temporary directory
rmtree(tmpd)
rmagic = Rmagic()
ri.set_writeconsole(rmagic.write_console)
if __name__ == '__main__':
snippet = '''
a=lm(Y~X)
print(summary(a))
plot(X, Y, pch=23, bg='orange', cex=2)
plot(Y, X)
print(summary(X))
r = resid(a)
'''
opts = '--bg="gray" --width=700 --inputs=X,Y --outputs=r'
# for now, this is a placeholder that will eventually be
# a full ipython shell
#
# it is only used to retrieve the value of the variables to
# be assigned as inputs into R
rmagic.shell = {'X': np.random.standard_normal(40),
'Y': np.random.standard_normal(40)}
result = rmagic.cell_magic(opts, snippet)