Monday, May 6, 2013

Visualizing DSQ on Facebook with Processing js

For my final project I worked with Angus and Saiph Savage (a phD student at University of Carlifornia, Santa Barbara) to create an interface for visualizing a model for Directed Social Queries on Facebook. Saiph created the LDA topic model which predicted the Facebook friends most likely to answer a user's social query on Facebook based on the likes made by the user's friends.

Usually facebook apps that allow you to ask a query "Who is up for a movie tonight?" return a list of friends that are most likely answer your question. These friends are often accompanied by the top topics they (might) relate to, but the app does not show how it arrived at this conclusion. This visualization enables the user to explore the data and the process through which the model came up with its results.

In the beginning, the interface first shows all the topics related to the user's friends. These include "Television," "Fashion," "Music" etc.



 Rolling over the topics shows the facebook labels and words/tags associated with the topic:


 When the user enters something in the search box, for example, "pizza," the interface shows the top topic (in this case "food") and the top nine friends associated with the query and topic:



Rolling over a friend gives the likes the the friend made that most contribute to the topic. For example, rolling over "Rodrigo Zea" shows likes such as "skittles" and "pringles"


When the user rolls over one of the likes, a sidebar pops up on the left that gives further information about the like:



Lastly, rolling over a topic gives the same information as before the query--words and Facebook labels associated with the like.



Thus this visualization helps the user explore the data and helps them make decisions based on the query. For example, if the user is organizing a charity fashion show, this interface can help them figure out which Facebook friends are most likely to respond to their cause.

Project Evolution 


In my presentation I mostly talked about the experience I had while working on this project with Angus and Saiph. Here's how it evolved: 
a)     Directed Social Queries 
      Earlier, Angus and Saiph had worked on a static version of the visualization, which showed all the data at once for a small number of friends. In this visualization friends could be linked to likes, likes to words, and words to topic. It was also possible to go in the opposite direction as well:
   

   

b) First Visualization
      Then we started working on the interface Saiph wanted for her model. The first task in that involved connecting all data (topic, words, likes, topics) to everything else. This gave us the flexibility to visualize the data in whichever way we wanted to. This took the longest time because there were a lot of different types of files and a lot of different types of data.
     When we started visualizing the data, the first visualization we did was to show all friends, and their top three topics.
     Clicking on the friend showed the likes, and clicking on likes showed the words:
   

    Clicking on the top topic lead you to words, followed by likes and friends:
 
 

    Our idea was to make each node draggable, and to give the user the ability to pin words/topics/likes/friends that they were interested in. This would give them full freedom to explore the data, and compare with other friends/topics. I even came up with an algorithm to do it, but before we could implement it Saiph said she wanted something really simple in processing js, which is when we switched tracks completely.

 c) First Visualization with Processing js
     The first visualization we did in Processing js was very simple--it just showed the top 50 friends, color-coded according to rank. Rolling over them showed their likes. 
       

      
Saiph really liked this one, but she wanted to have more information about the likes and the topics, which is how the final version came to be. 

How it works 


When the search button is pressed, a python script is called, which in turn creates a json file which looks like this:
http://ec2-107-22-157-20.compute-1.amazonaws.com/transparentInterface/transparent.py?question=pizza

Our first task was to pass this data into the Processing sketch.

Passing data from javascript/html to Processing 
The html file (which can be seen by viewing the source code at http://ec2-107-22-157-20.compute-1.amazonaws.com/transparentInterface/) calls a javascript function called doSearchTest(), which in turn
uses the ajax framework to open a XML HTTP request to the json file given above. Since it is on the same server, it does not create a problem with the same origin policy. The ajax code is:

        $.ajax({
           type: 'GET',
           contentType: "application/json",
           dataType: "text",
           url: 'http://ec2-107-22-157-20.compute-1.amazonaws.com/transparentInterface/transparent.py?question='+q,
           success: function(data) {
              formatResultsSC(data,keyword);
   },
        });

On success, this code reads the json file, puts it into a string, and passes it to formatResultsSC(), which in turn calls the processing sketch:

function formatResultsSC(data, keyword) {
        $('#loading').fadeOut();
var pjs = Processing.getInstanceById('DSQ');
             
                pjs.passDataIntoProcessing(data);              
             
        }

Here, pjs is an instance of the processing sketch. The statement pjs.passDataIntoProcessing(data) calls the method in the processing sketch, which looks something like this:

void passDataIntoProcessing (String data) {
    //code to parse the JSON data and make data ready for visualization
    dataIsReady = true;
}

Here dataIsReady is a global variable. The draw method uses this boolean to determine what to draw:

void draw() {
       if(dataIsReady()) {
            for(Friend f: friends) {
               f.render();
            }
            //render all friends and topics
      }
      else {
          //render the topics only
      }
}

Mouse Over functions
The rollover functions are all part of the render() methods in Friend/Topic class. Both these classes look something like this:

public class Friend {
       //instance variables for position of ellipse and name of friend
       int x, y, w, h;
       String friend;
     
       //boolean value to check is mouse is over the friend
       boolean isOver;

      public Friend() {
          //constructor
      }

      public void render() {
          if(isOver) {
               //render friends AND likes
               if( likeIsOverFunc(likeX, likeY)
                   //draw sidebar
          }
           else {
              //render friends only
          }
     }

     likeIsOverFunc(likeX, likeY) checks if the X and Y co-ordinates of the mouse are within the range of the
     likes for the friend.
     The isOver value is changed in the mouseMoved method:
     void mouseMoved() {
          for(Friend f: friends) {
               if(f.isOverFunc()) {
                    f.isOver = true;
               }
          }
     }

(The mouseMoved method also ensures only one friend node is open at any given time, the code for which is not included here).

Passing data from Processing js to javascript 
Another thing the processing sketch does is that clicking on a friend passes the user ID of the friend to the javascript, which adds their profile picture to the bar at the bottom of the visualization. To do this, the Processing sketch has the following code to let it know about the javascript:


interface JavaScript {
      //name of the global javascript function to be called
      void B(String name1, int idNumber);

 }

  void bindJavascript(JavaScript js) {
       javascript = js;
 }
 JavaScript javascript;

The mousePressed() method calls the javascript function:
void mousePressed() {
      for( Friend f: friends) {
           if ( f.isClicked) {
                  //call the javascript function and pass in the data
                   javascript.B ( f.name, f.userID)
           }
       }
}

The javascript code also has to know about the Processing sketch:

 <script type="text/javascript">
        var bound = false;
   
         //This function checks if processing js has loaded the sketch yet. If it has, it tells the sketch what  
        // "javascript" should be. If the sketch is not loaded yet, it checks again after 250 ms.
        function bindJavascript() {
                var pjs = Processing.getInstanceById('DSQ');
                 if(pjs!=null) {
      pjs.bindJavascript(this);
                       bound = true; }
                if(!bound) setTimeout(bindJavascript, 250);
       }
        bindJavascript();

          //this is the global javascript function that is called from the processing sketch
 function B(name1, idNumber) {
      //code for putting profile picture in the bottom bar
   
 }
  </script>

More information about this can be found in this tutorial: http://processingjs.org/articles/PomaxGuide.html#sketchtojs