5

I'm trying to run the following R code through my Django application with the end result being a printed R graph in a Python Django webpage. Here is the code for R.

t=read.table(file=file("request.FILES['fileUpload']"))
colnames(t) <- c('x', 'y')
t = data.frame(t)
fit1 = lm(y ~ x, data = t)
par(mfrow=c(1,1))
plot(x=t$x, y=t$y, xlab="x", ylab="y", main="Simple Linear Regression", xlim=c(0,100), ylim=c(0,6), par=20)
abline(fit1, col="red")

Here is something like what I am trying to achieve in the Django function.

from django.shortcuts import render, HttpResponse
import pandas as pd

def upload_files(request):
    if request.method == 'POST':
        upload = pd.read_table(request.FILES['fileUpload'])
        << Run R Code Here and return the graph >>
        response = RGraph
        return response
             OR
        return render(request, 'Regression/index.html', {'graph':response})
    return render(request, 'Regression/index.html')

HTML code is as follows.

<html>
    <title>Import File</title>
        <body>
            <h1>Import File</h1>
                <hr>
            {% if graph %}
                <img alt="my base64 graph" src="data:image/png;base64,{{graph}}" />
            {% endif %}
            <form enctype="multipart/form-data" method="post">
                {% csrf_token %}
                <input type="file" name="fileUpload" />
                <input type="submit" value="Submit" />
            </form>
        </body>
</html>

As always, thanks for the help.

Ravaal
  • 3,233
  • 6
  • 39
  • 66

3 Answers3

7

Apparently rpy2 does not provide a direct function which can return a file object to python. So what i recommend is:

1) Set a path to save your R image files

  • On your settings.py define a variable where your R scripts/images should be saved

    STATIC_R = 'r_plots'
    

2) Build a model/form to handle the file management based on your settings

  • Model

    from django.conf import settings
    
    class RScript(models.Model):
        script = FileField(upload_to=settings.STATIC_R)
    
        @property
        def script_path(self):
            return os.path.basename(self.script.name)
    
  • Remember (from docs): FielField.upload_to: A local filesystem path that will be appended to your MEDIA_ROOT setting to determine the value of the url attribute.

  • Form

    class RScriptForm(forms.ModelForm):
        class Metal:
            model = RScript
            fields = ('script',)
    

3) Running you code from upload

  • Receive your R script and save it

    my_plot_script = '''
        t=read.table(file=file("{path}"))
        colnames(t) <- c('x', 'y')
        t = data.frame(t)
        fit1 = lm(y ~ x, data = t)
        par(mfrow=c(1,1))
        png(filename="{path}.png")
        plot = plot(x=t$x, y=t$y, xlab="x", ylab="y", main="Simple Linear Regression", xlim=c(0,100), ylim=c(0,6), par=20)
        abline(fit1, col="red")
        dev.off()
    '''
    
    def my_view(request):
        if request.method == 'POST':
            form = RScriptForm(request.POST)
            if form.is_valid():
                form.save()
                (...)
    
  • Now that we have script saved let's try to running it with rpy2

    my_plot_script = '''
        t=read.table(file=file("{path}"))
        colnames(t) <- c('x', 'y')
        t = data.frame(t)
        fit1 = lm(y ~ x, data = t)
        par(mfrow=c(1,1))
        png(filename="{path}.png")
        plot = plot(x=t$x, y=t$y, xlab="x", ylab="y", main="Simple Linear Regression", xlim=c(0,100), ylim=c(0,6), par=20)
        abline(fit1, col="red")
        dev.off()
    '''
    
    def my_view(request):
        context = {}
        if request.method == 'POST':
            form = RScriptForm(request.POST)
            if form.is_valid():
                form.save()
                import rpy2.robjects as robjects
                robjects.r(my_plot_script.format(form.instance.script_path))
                context['graph'] = form.instance.script_path + '.png'
                return render(request, 'Regression/graph.html', context)
    
  • on your template

    <html>
        <title>Import File</title>
            <body>
                <h1>Import File</h1>
                    <hr>
                {% if graph %}
                    <img alt="my base64 graph" src="{{graph}}" />
                {% endif %}
                <form enctype="multipart/form-data" method="post">
                    {% csrf_token %}
                    <input type="file" name="fileUpload" />
                    <input type="submit" value="Submit" />
                </form>
            </body>
    </html>
    
Ramon Moraes
  • 477
  • 7
  • 23
  • Could you show me how to do this with the code that I posted? Please and thank you. – Ravaal Sep 21 '15 at 15:04
  • Ok. Just give me some minutes :) – Ramon Moraes Sep 21 '15 at 15:13
  • Thank you for taking the time to figure this out. I added the index.html HTML code in the OP. – Ravaal Sep 21 '15 at 15:29
  • @Poppins586 can you just say which of those line from the R script/func is the one generating the image to plot? I'm not familiar with R – Ramon Moraes Sep 21 '15 at 16:06
  • one generates the plot and the other puts linear regression on the plot. The two lines that do that are the last two lines in the code that I posted.--- plot(x=t$x, y=t$y, xlab="x", ylab="y", main="Simple Linear Regression", xlim=c(0,100), ylim=c(0,6), par=20) abline(fit1, col="red") – Ravaal Sep 21 '15 at 16:11
  • Your whole graph(file) function is written in R. I'm getting syntax errors >. – Ravaal Sep 21 '15 at 16:29
  • Ok. Now i got it. The @Shang Wang guy was right. You will need to save the plot as just a image on a folder covered by your static config. and then you just pass the string/name of the file to the __static__ tag on the template – Ramon Moraes Sep 21 '15 at 16:41
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/90251/discussion-between-vyscond-and-poppins586). – Ramon Moraes Sep 21 '15 at 17:18
3
  1. For this you can use the interface between R and Python given in the RPy2` python package. This allows you to run a R session alongside a python session, and being able to run R commands from within python and getting back the result.

  2. Alternatively, you can run R as a commandline script on the server, have a look at Rscript to get this working. This script could produce a png based on a number of input arguments. Python could then pick up the png and send it back to the user.

  3. As a third alternative, you could run R via Rserve, and create a connection to it to get the plotting done. For example see here.

Solution 3 is a bit overkill, but allows you to use R on a different server than your Django instance. Solution 1. is quite flexible, but more complex. Finally, solution 2. is the simplest solution, but sometimes lacks flexibility, especially if lot's of interaction between R and Python is needed.

Paul Hiemstra
  • 59,984
  • 12
  • 142
  • 149
  • Could you possibly show how I may go about doing this in code? This answer did not help me much. – Ravaal Sep 21 '15 at 14:58
1

If you install.package('magick') in your R instance then you can use rpy2 to get the image data back to Python as follows (I've used a simplier plot function):

from io import BytesIO

import PIL.Image as Image
import rpy2.robjects as ro

def main():
    r = ro.r

    r('''
        library("magick")
        figure <- image_graph(width = 400, height = 400, res = 96)
        plot(c(1,2,3))
        image <- image_write(figure, path = NULL, format = "png")
    ''')

    image_data = ro.globalenv['image']

    image = Image.open(BytesIO(bytes(image_data)))

    return image


if __name__ == '__main__':
    image = main()
    image.show()

You can then use the method here to render the image within a Django template.

isedwards
  • 2,429
  • 21
  • 29