MP Logo

Developing for Mixpanel

Mixpanel's developer platform lets developers build visualizations, reports, and tools against the data that already resides in Mixpanel to help companies understand their data in unique ways.

Custom reports

A Mixpanel custom report makes it easy to query, process, and view your data in new and powerful ways and then access it directly on Mixpanel like you would any other Mixpanel report. At the core, a custom report is a static HTML file that Mixpanel will load inside of an iframe on Mixpanel.com. When you create a new custom report, an index.html file will automatically be created that includes the mixpanel-platform JavaScript library.

An overview of the library

The mixpanel-platform library is a set of utilities to make querying and displaying your Mixpanel data as simple as possible. It includes commonly used controls like event dropdowns and date range pickers, simple methods for querying the Mixpanel APIs like segmentation and retention, as well as widgets for displaying your data such as line charts and tables.

Library dependencies

The mixpanel-platform library has a few dependencies that come bundled with it. This means you have access to these utilities out-of-the-box when writing custom reports:

jQuery (v2.1.1): great for DOM manipulation, event handling, and AJAX, among other things. Furthermore, we use it here at Mixpanel as a wrapper for our Platform API. A basic understanding of how jQuery works will go a long way in helping you build custom reports.

Underscore (v1.7.0): provides over 100 utility functions for cleanly and easily manipulating data.

Moment (v2.8.4): useful for parsing, validating, manipulating, and formatting dates.

In addition to these three, you can import any other JS library that might be of assistance.



Building a custom report

After you've created a new report, you'll be able to edit your custom report's index.html file directly on Mixpanel or by cloning the GitHub repository where your custom report lives. In general, a report is made up of a few things: querying your data, processing the results, and displaying it in a useful manner. The mixpanel-platform library makes all three of those things extremely easy. If you have worked with jQuery before this should look very familiar to you.

Full example:
<!doctype html>
<html>
    <head>
        <link rel="stylesheet" type="text/css" href="https://cdn.mxpnl.com/libs/mixpanel-platform/css/reset.css">
        <link rel="stylesheet" type="text/css" href="https://cdn.mxpnl.com/libs/mixpanel-platform/build/mixpanel-platform.v0.latest.min.css">
        <script src="https://cdn.mxpnl.com/libs/mixpanel-platform/build/mixpanel-platform.v0.latest.min.js"></script>
        <script>
            // create a line chart
            var lineChart = $('#lineChart').MPChart({chartType: 'line'});
            // create a dropdown pre-populated with events from the authenticated project
            $('#eventSelect').MPEventSelect().on('change', function(e, selectedEventName) {
                // when an event is chosen, run a segmentation query to get counts for that event
                MP.api.segment(selectedEventName).done(function(queryResults) {
                    // display the results of the query in a line chart
                    lineChart.MPChart('setData', queryResults);
                });
            });
        </script>
    </head>
    <body class="mixpanel-platform-body">
        <div class="mixpanel-platform-section">
            <div id="eventSelect" style="float: left;"></div>
            <div style="clear: both;"></div>
            <div id="lineChart" style="margin-top: 10px;"></div>
        </div>
    </body>
</html>


Specifying Mixpanel API credentials

In order to run queries via the MP.api global singleton, the mixpanel-platform library needs to know what API credentials to use which will determine what project data gets queried. There are a couple ways the API credentials can be registered:

Specifying credentials for local development

If you want to develop your custom report outside of the Mixpanel.com editor (e.g. in vim, an IDE, etc.), you have two options for passing in your API secret. The preferred way is as a URL parameter: http://localhost/my_custom_report/index.html?api_secret=your_projects_api_secret. This way you don't risk accidentally checking it into your Git repository. However, you can alternatively specify it by calling MP.api.setCredentials(projectSecret).

Specifying credentials when running your report on Mixpanel.com

When you run your report on Mixpanel.com, the credentials for the project you're running it under will get registered for you automatically. When your custom report's index.html is loaded, Mixpanel will send a message to it (via the Window.postMessage method) with the credentials. The mixpanel-platform library listens for the message and will proceed to register the credentials.



Specifying inputs

Most useful reports require some configuration from the user like specifying the event to use, the data range to query over, etc. The mixpanel-platform library contains a number of useful controls to make adding these inputs extremely easy. For example, if you want to allow a user to specify a date range you can do the following:

// assumes you have a div element on the page somewhere with id="datePicker"
$('#datePicker').MPDatepicker().on('change', function(event, dateRange) {
    console.log(dateRange.from);
    console.log(dateRange.to);
});


Querying Mixpanel

Once API credentials have been set, you can use the MP.api global singleton to run queries. All Mixpanel API read queries are supported. The lowest-level query method is query which allows you to specify an endpoint to query and the params to go along with it. Additionally, MP.api contains convenience methods to make querying the various APIs easier. The following example shows a simple segmentation query that segments the "Homepage View" event by the "$browser" property. Note that you can use .done to execute a callback when the query has completed.

Code
// assumes you have an event named "Homepage view" and a property named "$browser" in your project
MP.api.segment('Homepage view', '$browser').done(function(queryResults) {
    console.log(queryResults.values());
});
Result
{
    'Chrome': {
        '2010-10-01': 10,
        '2010-10-02': 21
    },
    'Firefox': {
        '2010-10-01': 14,
        '2010-10-02': 20
    }
}


Displaying your data

The mixpanel-platform library comes with a number of widgets you can use to display data in useful ways. For example, you can easily display data in a line chart by doing the following:

var data = {
    'Chrome': {
        '2010-10-01': 10,
        '2010-10-02': 21
    },
    'Firefox': {
        '2010-10-01': 14,
        '2010-10-02': 20
    }
};

// assumes you have a div element on the page somewhere with id="chart"
$('#chart').MPChart({chartType: 'line', data: data});


Building a report in your own development environment

Because Mixpanel Platform stores the source code for all your reports on GitHub, setting up a report for local development is easy. This will let you use your own code editor, webserver, and other code management tools.

The first thing you'll need to do is clone the repository from GitHub. Click the Open on GitHub button in the upper-right corner of the report editor:

Open your report on GitHub

Copy the repository URL from the HTTPS clone URL textbox, located in the right column of the repository page on GitHub, near the bottom:

Clone your repository on GitHub

Next you'll need to check out your report's source code. Open a terminal, navigate to the directory you want to store your report's source code in, and enter git clone followed by the URL you just copied:

Terminal session
$ cd ~
~ $ git clone https://github.com/mixpanel-platform/mixpanel-mixpanel-3-Jkj1owr.git
Cloning into 'mixpanel-mixpanel-3-Jkj1owr'...
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
Checking connectivity... done.

If you see this error, you need to install git. Take a look at the official git install page for more information. After that, check out GitHub's tutorial for setting up git to get code to and from GitHub.

~ $ git clone https://github.com/mixpanel-platform/mixpanel-mixpanel-3-Jkj1owr.git
-bash: git: command not found

Now that you have the source code checked out locally, you should run your report to make sure it's working. Enter the report's code directory, and you should see an index.html file:

~ $ cd mixpanel-mixpanel-3-Jkj1owr/
~/mixpanel-mixpanel-3-Jkj1owr $ ls
index.html

You need to run a local webserver from this directory to serve index.html to your browser. Python's SimpleHTTPServer is a good choice:

    ~/mixpanel-mixpanel-3-Jkj1owr $ python -m SimpleHTTPServer
    Serving HTTP on 0.0.0.0 port 8000 ...

Now open up a browser and navigate to http://localhost:8000/index.html (note that the /index.html is required - without it your API queries will fail). Your report is now running locally!

Your report is running in the browser!

But there's a problem - the report doesn't have your API secret so none of its API requests will work. The easiest way to give the report access to your secret is to pass it as a query parameter in the browser URL:

Your report is running in the browser and can make requests!

Alternatively, you can pass your API secret to the report through JavaScript as described in Specifying credentials for local development, but this isn't recommended because it risks publicly exposing your secret if you accidently check it in to your repository.

Now that your report is up and running locally, you should try making a change. Open the index.html file in a code editor of your choice, and add these lines to inspect the result of a query:

index.html
// lines truncated for brevity

         if (eventName) {
           MP.api.segment(eventName, propName, dateRange).done(function(results) {
             eventGraph.MPChart('setData', results);
             eventTable.MPTable('setData', results);
             console.log('Query results:'); // <== add this line
             console.log(results); // <== add this line
           });
         }

// lines truncated for brevity

If you chose "Blank slate" when you created your report, your index.html file will look more like this:

// lines truncated for brevity

 <body class="mixpanel-platform-body">
   <h1>Hello, World!</h1>
   <script>
       // Run queries and display results here
       console.log('Hello, world!') // <== add this line
   </script>

// lines truncated for brevity

Save the file, and go back to your terminal. You can verify the changes were applied by entering git diff:

~/mixpanel-mixpanel-3-Jkj1owr $ git diff
diff --git a/index.html b/index.html
index 709a040..e306eb5 100644
--- a/index.html
+++ b/index.html
@@ -35,6 +35,8 @@
             MP.api.segment(eventName, propName, dateRange).done(function(results) {
               eventGraph.MPChart('setData', results);
               eventTable.MPTable('setData', results);
+              console.log('Query results:');
+              console.log(results);
             });
           }
         };
})

Now you need to commit your changes. First, in order to tell git which changes should be part of the commit, stage them using git add:

~/mixpanel-mixpanel-3-Jkj1owr $ git add index.html

Next, commit the changes (-m "Add logging" tells git to use "Add logging" as the commit message, a description of the changes in the commit):

~/mixpanel-mixpanel-3-Jkj1owr $ git commit -m "Add logging"
[master f5bcdaf] Add logging
 1 file changed, 2 insertions(+)

Finally, you need to push your new commit back to the repository on GitHub:

~/mixpanel-mixpanel-3-Jkj1owr $ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 319 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://github.com/mixpanel-platform/mixpanel-mixpanel-3-Jkj1owr.git
   b981e45..f5bcdaf  master -> master

Let's verify that your code was successfully pushed to the repository. Open up (or refresh) your report's page on mixpanel.com, and select an event from the dropdown:

Your report is running on mixpanel.com!

Open up the JavaScript console to see whether the report has output the logging information you added. You can do this by pressing a hotkey, which will vary by operating system and browser:

Chrome Firefox Safari
Windows, Linux Control + Shift + J Control + Shift + K Control + Shift + I
Mac OS X Command + Option + J Command + Option + K Command + Option + C

If you're using Safari, you may need to activate the developer tools first. Go to Preferences -> Advanced (tab) and check the Show Develop menu in menu bar checkbox.

You should see a pane like this in your browser, which will display the data output by your report:

Your report is running on mixpanel.com!

Congratulations! You're now completely set up to make changes to your report locally, and push them live to mixpanel.com.



Using external libraries

If you want to use external libraries that don't already come with your custom report, you have two options using the <script> tag.

Import a publicly hosted version of the library:

<!doctype html>
<html>
    <head>
    <script src="https://somewebsite.com/their-library.js"></script>
    ...

Or commit a version of the library to your repository and refer to it with a relative path:

Commit d3 to your repository
<!doctype html>
<html>
    <head>
    <script src="./d3.min.js"></script>
    ...


Mixpanel Query API

topEvents MP.api.topEvents([params], [settings|callback])

Fetches the most common events in your project and returns a jqXHR object. This will query the following endpoint: https://mixpanel.com/api/2.0/events/names/. See the HTTP reference documentation.

Parameters
Name Type Required Description
params Object No Parameters for the query. See the full list of parameters.
settings Object No Settings for the jQuery Ajax call. If this parameter is used, then there can be no callback parameter.
callback Function No Optional callback to be executed upon completion of the jQuery Ajax call. If this parameter is used, then there can be no settings parameter.

Code 0
// optional
var params = {
    type: 'general',  // analysis type for the data, can be 'general', 'unique', or 'average'
    limit: 3          // maximum number of results to return
};

MP.api.topEvents(params).done(function(results) {
    console.log('top events\n', results.values());
});
Result 0
got results
top events
{
   0: 'Track',
   1: 'Login',
   2: 'Purchase'
}
Code 1
MP.api.topEvents({from: 'today', limit: 2}).done(function(results) {
    console.log('top events\n', results.values());
});
Result 1
top events
{
    0: {
        amount: 1203,
        event: 'Track',
        percent_change: 0.012222
    },
    1: {
        amount: 604,
        event: 'Login',
        percent_change: -0.3002
    }
}


topProperties MP.api.topProperties(event, [params], [settings|callback])

Gets the top property names and counts for an event and returns a jqXHR object. This will query the following endpoint: https://mixpanel.com/api/2.0/events/properties/top/. See the HTTP reference documentation.

Parameters
Name Type Required Description
event String Yes The event to query.
params Object No Parameters for the query. See the full list of parameters.
settings Object No Settings for the jQuery Ajax call. If this parameter is used, then there can be no callback parameter.
callback Function No Optional callback to be executed upon completion of the jQuery Ajax call. If this parameter is used, then there can be no settings parameter.

Code
// optional
var params = {
    limit: 100    // maximum number of results to return
};

MP.api.topProperties('Track', params).done(function(results) {
    console.log('top properties\n', results.values());
});
Result
top properties
{
    age: 14849,
    gender: 14849,
    favorite color: 543
}


propertyValues MP.api.propertyValues(event, property, [params], [settings|callback])

Gets the top values for a property and returns a jqXHR object. This will query the following endpoint: https://mixpanel.com/api/2.0/events/properties/values/. See the HTTP reference documentation.

Parameters
Name Type Required Description
event String Yes The event which has the property for the query.
property String Yes The property for which to get the top values.
params Object No Parameters for the query. See the full list of parameters.
settings Object No Settings for the jQuery Ajax call. If this parameter is used, then there can be no callback parameter.
callback Function No Optional callback to be executed upon completion of the jQuery Ajax call. If this parameter is used, then there can be no settings parameter.

Code
// optional
var params = {
    limit: 3      // maximum number of results to return
};

MP.api.propertyValues('Track', '$browser', params).done(function(results) {
    console.log(results.values());
});
Result
{
    0: 'Chrome',
    1: 'Firefox',
    2: 'Safari'
}


segment MP.api.segment(event, [property], [params], [settings|callback])

Gets data for an event, segmented and filtered by properties, and returns a jqXHR object. The property parameter can be the name of a property (e.g. "gender") you wish to segment by or a more complicated expression. This will query the following endpoint: https://mixpanel.com/api/2.0/segmentation/. See the HTTP reference documentation.

Parameters
Name Type Required Description
event String Yes The event to query.
property String No The property to segment.
params Object No Parameters for the query. See the full list of parameters.
settings Object No Settings for the jQuery Ajax call. If this parameter is used, then there can be no callback parameter.
callback Function No Optional callback to be executed upon completion of the jQuery Ajax call. If this parameter is used, then there can be no settings parameter.

Example params
var params = {
    from: moment().subtract(30, 'days'),    // the earliest date you'd like to include in the query
    to: moment(),                           // the latest date you'd like to include in the query
    limit: 100,                             // maximum number of results to return
    type: 'general',                        // analysis type for the data, can be 'general', 'unique', or 'average'
    unit: 'day',                            // level of granularity of the data, can be 'minute', 'hour', 'day', or 'month'

    // the following are undefined by default
    'where': 'properties["age"] == 21',     // an expression to filter by. See event filters
    'method': 'average',                    // string specifying a way to aggregate the results, can be 'numeric', 'sum', or 'average'
    'buckets': 4,                           // the number of buckets to return in a 'numeric' query
};

Code 0
// assumes you have an event called 'Track' and a property called '$browser'
MP.api.segment('Track', '$browser', {from: moment().subtract(1, 'days'), unit: 'day'}).done(function(results) {
    console.log(results.values());
});
Result 0
{
    Chrome: {
        2010-10-01: 10,
        2010-10-02: 21
    },
    Firefox: {
        2010-10-01: 14,
        2010-10-02: 20
    }
}
Code 1
// assumes you have an event called 'Track' and a property called 'Profile completeness'
MP.api.segment('Track', 'Profile completeness', {from: moment().subtract(1, 'days'), method: 'numeric', buckets: 4}).done(function(results) {
    console.log(results.values());
});
Result 1
{
    0-25: {
        2010-10-02: 21
    },
    26-50: {
        2010-10-02: 30
    },
    51-75: {
        2010-10-02: 30
    },
    76-100: {
        2010-10-02: 5
    }
}
Code 2
// assumes you have an event called 'Purchase' and properties 'Price' and 'Discount'
MP.api.segment('Purchase', 'properties["Price"] - properties["Discount"]', {from: moment().subtract(4, 'days'), method: 'sum'}).done(function(results) {
    console.log(results.values());
});
Result 2
{
    2010-09-29: 545.0,
    2010-09-30: 120.0,
    2010-10-01: -302.0,
    2010-10-02: 1231.0
}
Code 3
// assumes you have an event called 'Purchase' and a property called 'Age'
MP.api.segment('Purchase', 'Age', {from: moment().subtract(4, 'days'), method: 'average'}).done(function(results) {
    console.log(results.values());
});
Result 3
{
    2010-09-29: 45.62,
    2010-09-30: 41.2,
    2010-10-01: 24.33,
    2010-10-02: 36.54
}


events MP.api.events(*event, [params], [settings|callback])

Performs an events query with an arbitrary list of events and returns a jqXHR object. This gets the number of times each event occured on each day covered by the query. This will query the following endpoint: https://mixpanel.com/api/2.0/events/. See the HTTP reference documentation.

Parameters
Name Type Required Description
event String Yes The event(s) to query. An arbitrary number of comma separated events can be queried.
params Object No Parameters for the query. See the full list of parameters.
settings Object No Settings for the jQuery Ajax call. If this parameter is used, then there can be no callback parameter.
callback Function No Optional callback to be executed upon completion of the jQuery Ajax call. If this parameter is used, then there can be no settings parameter.

Code
// optional
var params = {
    type: 'unique',     // analysis type for the data, can be 'general', 'unique', or 'average'
    unit: 'day',        // level of granularity of the data, can be 'minute', 'hour', 'day', 'week', 'month'
    interval: 2         // number of "units" back to include
};

MP.api.events('Track', 'Purchase', params).done(function(results) {
    console.log('got results', results.values());
});
Result
got results
{
    'Track': {
        '2010-05-30': 10,
        '2010-05-31': 110
     },
     'Purchase': {
         '2010-05-30': 3,
         '2010-05-31': 32
     }
}


funnel MP.api.funnel(*event, [params], [settings|callback])

Performs a funnels query and returns a jqXHR object. Retrieves the count of each subsequent event performed within a time window. Events can be strings or objects containing an event and a filter. This will query the following endpoint: https://mixpanel.com/api/2.0/arb_funnels/.

Parameters
Name Type Required Description
event String|Object Yes The event(s) to query. An arbitrary number of comma-separated events can be queried. Events can be strings or objects of the form {event: eventName, selector: expression}. The selector expression is optional and will allow you to more finely control the events that are included in your funnel. For a detailed description of the expression syntax, look here.
params Object No Parameters for the query.
settings Object No Settings for the jQuery Ajax call. If this parameter is used, then there can be no callback parameter.
callback Function No Optional callback to be executed upon completion of the jQuery Ajax call. If this parameter is used, then there can be no settings parameter.

Example params
var eventObject = {
    event: 'Signup',
    selector: 'properties["age"] == 21'
};

var params = {
    from: moment().subtract(1, 'months'),
    to: moment(),
    limit: 100,                             // maximum number of results to return
    length: 14,                             // number of days each user has to complete the funnel

    // the following are undefined by default
    where: 'properties["age"] == 21',       // an expression to filter by.
    segment: '$browser'                     // a segmentation expression or event property.
};

Code
// assumes you have events 'Signup' and 'Purchase'
MP.api.funnel('Signup', 'Purchase').done(function(results) {
    console.log(results);
});
Result
[
    {
        2010-09-15: {
            avg_time: null,
            count: 432,
            event: "Signup",
            goal: "Signup",
            overall_conv_ratio: 1,
            step_conv_ratio: 1
        }
    },
    {
        2010-09-15: {
            avg_time: 2,
            count: 140,
            event: "Purchase",
            goal: "Purchase",
            overall_conv_ratio: 0.32407407407407407,
            step_conv_ratio: 0.32407407407407407
        }
    },
    {
        2010-09-15: {
            avg_time: null,
            count: 38,
            event: "Purchase",
            goal: "Purchase",
            overall_conv_ratio: 0.08796296296296297,
            step_conv_ratio: 0.2714285714285714
        }
    }
]


retention MP.api.retention(event, [params], [settings|callback])

Performs a retention query and returns a jqXHR object. Retrieves the number of times users performed an event within intervals of time. This query can contain a filter. This will query the following endpoint: https://mixpanel.com/api/2.0/retention/. See the HTTP reference documentation.

Parameters
Name Type Required Description
event String Yes The event to query.
params Object No Parameters for the query. See the full list of parameters.
settings Object No Settings for the jQuery Ajax call. If this parameter is used, then there can be no callback parameter.
callback Function No Optional callback to be executed upon completion of the jQuery Ajax call. If this parameter is used, then there can be no settings parameter.

Example params
var params = {
    from: moment().subtract(1, 'months'),
    to: moment(),
    unit: 'day',                      // specifies the the bucket size; can be 'day' or 'week'
    interval_count: 1,                // the number of desired units
    limit: 100,                       // maximum number of results to return

    // the following are undefined by default
    born_event: 'Signup',             // if present, an event that must be tracked within the time frame before the user can be included in the query
    where: 'properties["age"] == 21', // an expression to filter by
    segment: '$browser',              // a segmentation expression or event property
};

Code
// assumes you have an event called 'Track'
MP.api.retention('Track', {from: moment().subtract(1, 'days'), inverval_count: 2}).done(function(results) {
    console.log(results.values());
});
Result
{
    2010-10-13: {
        first: 8273,
        counts: {
            0: 6737,
            1: 4077
        }
    },
    2010-10-12: {
        first: 4853,
        counts: {
            0: 3102,
            1: 2107
        }
    }
}


people MP.api.people([params], [settings|callback])

Performs a People query and returns a jqXHR object. Retrieves the project's People data. This query can contain a filter. This will query the following endpoint: https://mixpanel.com/api/2.0/engage/. See the HTTP reference documentation.

Parameters
Name Type Required Description
params Object No Parameters for the query. See the full list of parameters.
settings Object No Settings for the jQuery Ajax call. If this parameter is used, then there can be no callback parameter.
callback Function No Optional callback to be executed upon completion of the jQuery Ajax call. If this parameter is used, then there can be no settings parameter.

Code
MP.api.people({where: 'properties["$first_name"] == "Kyle"'}).done(function(results) {
    console.log(results.values());
});
Result
{
    page: 0,
    page_size: 1000,
    results: {
        0: {
            $distinct_id: "12345",
            $properties: {
                $first_name: "Kyle",
                $last_name: "War",
                $age: 42,
                levels_completed: 34,
                ghosts_created: 7,
                pizzas_made: 867
            }
        },
        1: {
            $distinct_id: "72648",
            $properties: {
                $first_name: "Kyle",
                $last_name: "Ren",
                $age: 65,
                levels_completed: 2,
                ghosts_created: 53,
                pizzas_made: 23
            }
        }
    },
    session_id: "1422536509-JCnfDV",
    status: "ok",
    total: 2
}


jql MP.api.jql(script, [params], [settings])

Performs a JQL query and returns a jqXHR object. See JQL documentation.

Parameters
Name Type Required Description
script Function|String Yes The JQL script to run.
params Object No Parameters for the query. See https://mixpanel.com/help/reference/jql/api-reference#api/params.
settings Object No Settings for the jQuery Ajax call.

Code
MP.api.jql(
    function main() {
        return Events({from_date: '2015-01-01', to_date: '2015-06-01'})
            .reduce(mixpanel.reducer.count())
    }
).done(function(results) {
    console.log(results);
});
Result
[1234]


option MP.api.option(key, val)

Sets options on the MP.api object for queries.

VALID OPTIONS
Option name Default Description
apiHost 'https://mixpanel.com' Send queries to apiHost instead of the default.

Code
MP.api.option('apiHost', 'https://myhost.com');


Data manipulation

While it's possible to pass your query results directly to a chart or table, it's often more useful to be able to perform additional calculations or transformations with your data first. The mixpanel-platform library has a number of utility methods to make this easy including, but not limited to, avg(), sum(), and values(), called via an instance of the MP.Data object.

The MP.Data object is a wrapper for your raw data. All data returned by a mixpanel-platform query are instances of the MP.Data object, but you can also wrap raw data yourself like so:

var mpdata = MP.Data.inst([1, 2, 3]); // Wrap raw data in MP.Data object
mpdata.values();                      // Returns [1, 2, 3]
mpdata.avg();                         // Returns an instance of MP.Data with raw data = 2
mpdata.avg().values();                // Returns 2
mpdata.sum().values();                // Returns 6

With the exception of the values() method, which returns the raw data, each MP.Data method returns another instance of an MP.Data object with the modified data. This means MP.Data methods are chainable. For example:

var mpdata = MP.Data.inst({a: [1, 3], b: [2, 4]}); // Wrap raw data in MP.Data object
mpdata.avg();                                      // Returns an instance of MP.Data with raw data = {a: 2, b: 3}
mpdata.sum();                                      // Returns an instance of MP.Data with raw data = {a: 4, b: 6}
mpdata.avg().sum().values();                       // Returns 5
mpdata.sum().avg().values();                       // Returns 5
mpdata.avg().avg().values();                       // Returns 2.5
mpdata.sum().sum().values();                       // Returns 10

MP.Data can contain raw data that is either a flat array (1) or an arbitrarily-deep nested object (2). Note that an arbitrarily-deep nested object can have array leaves (3):

// 1
[1, 2, 3]

// 2
{
a: {
    b: {
        c: 1,
        d: 2
    }
},
e: {
    f: {
        g: 1,
        h: 2
    }
}
}

// 3
{
a: {
    b: [1, 2]
},
c: {
    d: [1, 2]
}
}


Methods

Name Signature Return type Description
avg data.avg() MP.Data Returns a new MP.Data instance where each object or array leaf has been reduced to an average of its numerical values. See below for examples.
choose data.choose(segments) MP.Data Returns a new MP.Data instance containing only segments from the passed-in segments array. segments should be an array of strings representing top-level keys (segments) in the MP.Data instance. See below for examples.
combine MP.Data.combine(data1, data2) MP.Data Combines two MP.Data objects. It does a shallow copy of each MP.Data instance and returns a new one. If the two objects have overlapping keys, data1's values will be overwritten by data2's. Note that this is a static method. See below for examples.
divide dividend.divide(divisor) MP.Data Returns a new MP.Data instance where each leaf value is the quotient of its respective two mirror leaves in dividend and divisor. dividend and divisor must be of equal depth and their keys must match. If those two criteria aren't met, dividend is returned. See below for examples.
filter data.filter(filter) MP.Data Returns a new MP.Data instance with leaf values filtered by the filter method. filter must be a function that takes one parameter and returns true or false. See below for examples.
inliers data.inliers(from, to) MP.Data Returns a new MP.Data instance only containg leaf values such that each value >= from and <= to. See below for examples.
multiply multiplicand.multiply(multiplier) MP.Data Returns a new MP.Data instance where each leaf value is the product of its respective two mirror leaves in multiplicand and multiplier. multiplicand and multiplier must be of equal depth and their keys must match. If those two criteria aren't met, multiplicand is returned. See below for examples.
sum data.sum() MP.Data Returns a new MP.Data instance where each array or object leaf has been reduced to the sum of its numerical values. See below for examples.
values data.values() * Returns the raw data of the MP.Data instance. All arrays in the raw data will be converted to objects (i.e. the array indices become object keys). If it was an object, a copy of the object will be returned. Otherwise, the raw data will be returned directly (e.g. in the case of a number of string). See below for examples.


Examples


avg

var mpdata = MP.Data.inst([1, 2, 3]);
mpdata.avg().values(); // Returns 2

mpdata = MP.Data.inst({a: 2, b: 3, c: 10});
mpdata.avg().values(); // Returns 5

mpdata = MP.Data.inst({a: {b: 1, c: 2}});
mpdata.avg().values(); // Returns {a: 1.5}

choose

var mpdata = MP.Data.inst([42113, 92018, 519321]);
mpdata.choose(['0', '2']).values(); // Returns {0: 42113, 2: 519321}

mpdata = MP.Data.inst({a: 2, b: 3, c: 10});
mpdata.choose(['a']).values(); // Returns {a: 2}

mpdata = MP.Data.inst({a: 1});
mpdata.choose(['b']).values(); // Returns {}

combine

MP.Data.combine({a: 1}, {b: 2}).values();       // Returns {a: 1, b: 2}
MP.Data.combine({a: 1, b: 3}, {a: 2}).values(); // Returns {a: 2, b: 3}
MP.Data.combine([1, 2], [3, 4]).values();       // Returns {0: 3, 1: 4}

divide

var dividend = MP.Data.inst([3, 6, 9]);
var divisor = MP.Data.inst([3, 2, 4]);
dividend.divide(divisor).values(); // Returns {0: 1, 1: 3, 2: 2.25}

dividend = MP.Data.inst({a: 6});
divisor = MP.Data.inst({a: 3});
dividend.divide(divisor).values(); // Returns {a: 2}

dividend = MP.Data.inst({a: {b: 10, c: 20}});
divisor = MP.Data.inst({a: {b: 5, c: 4}});
dividend.divide(divisor).values(); // Returns {a: {b: 2, c: 5}}

dividend = MP.Data.inst({a: {b: 10, d: 20}});
divisor = MP.Data.inst({a: {b: 5, e: 4}});
dividend.divide(divisor).values(); // Returns {a: {b: 2, d: 20}}

dividend = MP.Data.inst({a: 6});
divisor = MP.Data.inst({b: 3});
dividend.divide(divisor).values(); // Returns {a: 6}

dividend = MP.Data.inst({a: {b: 6}});
divisor = MP.Data.inst({b: 3});
dividend.divide(divisor).values(); // Returns {a: {b: 6}}

dividend = MP.Data.inst({a: {b: 6}});
divisor = MP.Data.inst({a: {b: {c: 3}}});
dividend.divide(divisor).values(); // Returns {a: {b: NaN}}

filter

var mpdata = MP.Data.inst([1, 2, 3]);
var myFilter = function(val) {
return val > 2;
};
mpdata.filter(myFilter).values(); // Returns {2: 3}

mpdata = MP.Data.inst({a: {b: 1, c: 2}});
myFilter = function(val) {
return val % 2 === 0;
};
mpdata.filter(myFilter).values(); // Returns {a: {c: 2}}

mpdata = MP.Data.inst({a: {b: 1, c: 2}});
myFilter = function(val) {
return false;
};
mpdata.filter(myFilter).values(); // Returns null

inliers

var mpdata = MP.Data.inst([1, 2, 3]);
mpdata.inliers(1, 2).values(); // Returns {0: 1, 1: 2}

mpdata = MP.Data.inst({a: {b: 1, c: 2}});
mpdata.inliers(0, 4).values(); // Returns {a: {b: 1, c: 2}}

mpdata = MP.Data.inst({a: {b: 1, c: 2}});
mpdata.inliers(1, -1).values(); // Returns null

// Note that this is essentially a wrapper for:
mpdata = MP.Data.inst({a: {b: 1, c: 10}});
var low = 1;
var high = 1;
var myFilter = function(val) {
return val >= low && val <= high;
};
mpdata.filter(myFilter).values(); // returns {a: {b: 1}}

multiply

var multiplicand = MP.Data.inst([3, 6, 9]);
var multiplier = MP.Data.inst([3, 2, 4]);
multiplicand.multiply(multiplier).values(); // Returns {0: 9, 1: 12, 2: 36}

multiplicand = MP.Data.inst({a: 6});
multiplier = MP.Data.inst({a: 3});
multiplicand.multiply(multiplier).values(); // Returns {a: 18}

multiplicand = MP.Data.inst({a: {b: 10, c: 20}});
multiplier = MP.Data.inst({a: {b: 5, c: 4}});
multiplicand.multiply(multiplier).values(); // Returns {a: {b: 50, c: 80}}

multiplicand = MP.Data.inst({a: {b: 10, d: 20}});
multiplier = MP.Data.inst({a: {b: 5, e: 4}});
multiplicand.multiply(multiplier).values(); // Returns {a: {b: 50, d: 20}}

multiplicand = MP.Data.inst({a: 6});
multiplier = MP.Data.inst({b: 3});
multiplicand.multiply(multiplier).values(); // Returns {a: 6}

multiplicand = MP.Data.inst({a: {b: 6}});
multiplier = MP.Data.inst({b: 3});
multiplicand.multiply(multiplier).values(); // Returns {a: {b: 6}}

multiplicand = MP.Data.inst({a: {b: 6}});
multiplier = MP.Data.inst({a: {b: {c: 3}}});
multiplicand.multiply(multiplier).values(); // Returns {a: {b: NaN}}

sum

var mpdata = MP.Data.inst([1, 2, 3]);
mpdata.sum().values(); // Returns 6

mpdata = MP.Data.inst({a: 2, b: 3, c: 10});
mpdata.sum().values(); // Returns 15

mpdata = MP.Data.inst({a: {b: 1, c: 2}});
mpdata.sum().values(); // Returns {a: 3}

values

var mpdata = MP.Data.inst([40, 6, 15]);
mpdata.values(); // {0: 40, 1: 6, 2: 15}

mpdata = MP.Data.inst({a: 2, b: 3, c: 10});
mpdata.values(); // Returns {a: 2, b: 3, c: 10}

mpdata = MP.Data.inst(5);
mpdata.values(); // Returns 5

mpdata = MP.Data.inst('not useful data');
mpdata.values(); // Returns 'not useful data'


UI controls

MPSelect $('<div>').MPSelect(options);

Decorates the specified jQuery object with a dropdown containing user-specified data. Returns a jQuery object.

Options
Name Type Required Description
items Array Yes The data for the dropdown to display and pass to event handlers. Must be an array of objects with label and value keys: [{value: 1, label: "Item 1"}, {value: 2, label: "Item 2"}]. See the example code below for details.

Methods
Name Signature Return type Description
val $('mySelector').val([someValue]) * If the method is called with no parameters, it will return the dropdown's current value. If a parameter is passed, it will set the dropdown's value and return the jQuery object. The passed-in value must be one of the dropdown's options.

Events
Name Description
change Fired when the dropdown's selected value changes.

Code
var options = {
  items: [
    {label: 'Label 1', value: 1},
    {label: 'Label 2', value: 'Value 2'},
    {label: 'Third label', value: '3rd value'}
  ]
};

$(document).ready(function() {
    var dropdown = $('<div></div>').appendTo('body').MPSelect(options);     // Create the dropdown with your data
    dropdown.on('change', function(e, selection) {                          // Do something when an option is selected
        alert(selection);
    });
});
Demo


MPEventSelect $('<div>').MPEventSelect();

Decorates the specified jQuery object with a dropdown containing your project's top 100 events. Returns a jQuery object.

Options
None

Methods
Name Signature Return type Description
val $('mySelector').val([someValue]) * If the method is called with no parameters, it will return the dropdown's current value. If a value is passed, it will set the dropdown's value and return the jQuery object. The passed-in value must be one of the dropdown's options.

Events
Name Description
change Fired when the dropdown's selected value changes.

Code
var eventSelect = $('<div></div>').appendTo('body').MPEventSelect();  // Create the dropdown containing your top events
eventSelect.on('change', function(e, selection) {                     // Do something when an option is selected
    alert(selection);
});
Demo


MPPropertySelect $('<div>').MPPropertySelect();

Decorates the specified jQuery object with a dropdown that, after passed an event name, contains that event's top properties. Returns a jQuery object.

Options
None

Methods
Name Signature Return type Description
setEvent MPPropertySelect('setEvent', eventName) undefined Set the event name to populate the dropdown with that event's top properties. eventName must be a String and a valid event.
val $('mySelector').val([someValue]) * If the method is called with no parameters, it will return the dropdown's current value. If a parameter is passed, it will set the dropdown's value and return the jQuery object. The passed-in value must be one of the dropdown's options.

Events
Name Description
change Fired when the dropdown's selected value changes.

Code
// assumes you have an event in your project called 'Button clicked'
var eventName = 'Button clicked';
var propertySelect = $('<div></div>').appendTo('body').MPPropertySelect();  // Create the property dropdown
propertySelect.MPPropertySelect('setEvent', eventName);                     // Set the event name
propertySelect.on('change', function(e, selection) {                        // Do something when an option is selected
    alert(selection);
});
Demo


MPDatepicker $('<div>').MPDatepicker();

Decorates the specified jQuery object with a datepicker. Returns a jQuery object.

Options
None

Methods
Name Signature Return type Description
val $('mySelector').val([someValue]) * If the method is called with no parameters, it will return the datepicker's current value in the form of an object with to and from keys that map to native JavaScript Dates. If a parameter is passed, it will set the datepicker's to and from dates and return the jQuery object. The parameter must be of the same form as what is returned from val(), e.g.: {from: new Date(), to: new Date()}

Events
Name Description
change Fired when one of the datepicker's dates are changed.

Code
$(document).ready(function() {
    var dateSelector = $('<div></div>').appendTo('body').MPDatepicker();    // Create a date picker
    dateSelector.on('change', function(e, dates) {                          // Do something when the dates are changed
        alert('From: ' + dates.from + '\nTo: ' + dates.to);
    });
});
Demo



Visualizations

MPChart $('<div>').MPChart(options);

Decorates the specified jQuery object with a chart to visualize data. Returns a jQuery object.

Options
Name Type Required Description
chartType String Yes Valid options are line, bar, pie, and map.
data Object No data can be an Object of Objects containing String keys mapping numerical values, or it can be a flat Object of String keys mapping to numerical values. See the example code for details.
highchartsOptions Object No General options for the highcharts graph. See here for a full list of highcharts options (global and language options aren't available, but all the others are). Not available for map chart type.
normalized Boolean No Works only with {chartType: 'bar', stacked: true}. If true, each bar in the chart will be normailzed. If false, it will just be stacked. See below for an example.
stacked Boolean No Works only with {chartType: 'bar'}. If true, the bar chart will be stacked. If false, it won't. See below for an example.
tooltipFormatter Function No A function to format the text of the tooltip. Not available for map chart type.
xLabel String No Label for the x-axis. Not available for map chart type.
yLabel String No Label for the y-axis. Not available for map chart type.

Methods
Name Signature Return type Description
setData MPChart('setData', data) undefined Populates the chart with data. data can be an Object of Objects containing String keys mapping numerical values, or it can be a flat Object of String keys mapping to numerical values. See the example code for details.
addSeries MPChart('addSeries', series) undefined Adds a series of data to the chart. series should be of the same form as the rest of the data already in the chart (e.g., a "segment4" if looking at the line chart example just below).

Choose chart code and demo:

Code
var data = {
  'segment1': {
        '2010-09-01': 10,
        '2010-09-02': 11,
        '2010-09-03': 8
    },
    'segment2': {
        '2010-09-01': 1,
        '2010-09-02': 18,
        '2010-09-03': 17
    },
    'segment3': {
        '2010-09-01': 32,
        '2010-09-02': 37,
        '2010-09-03': 30
    }
};
$(document).ready(function() {
    var lineChart = $('<div></div>').appendTo('body').MPChart({chartType: 'line', highchartsOptions: {
        tooltip: {
            backgroundColor: '#fffce7'  // Make tooltip background yellow
        }
    }});                                // Create a line chart
    lineChart.MPChart('setData', data); // Set the chart's data
});

Demo
Code
var data = {
  'segment1': 10,
  'segment2': 20,
  'segment3': 40
};
$(document).ready(function() {
    var barChart = $('<div></div>').appendTo('body').MPChart({chartType: 'bar'});    // Create a bar chart
    barChart.MPChart('setData', data);                                               // Set the chart's data
});

Demo
Code
var params = {
    from: moment().subtract(7, 'days'),
    to: moment(),
    limit: 255
};

MP.api.segment('Button clicked', 'mp_country_code', params).done(function(results) {
    $('<div></div>').appendTo('body').MPChart({
        chartType: 'bar',
        data: results,
        stacked: true
    });
});

Demo
Code
var params = {
    from: moment().subtract(7, 'days'),
    to: moment(),
    limit: 255
};

MP.api.segment('Button clicked', 'mp_country_code', params).done(function(results) {
    $('<div></div>').appendTo('body').MPChart({
        chartType: 'bar',
        data: results,
        stacked: true,
        normalized: true
    });
});

Demo
Code
var data = {
  'segment1': 10,
  'segment2': 60,
  'segment3': 40
};
$(document).ready(function() {
    var pieChart = $('<div></div>').appendTo('body').MPChart({chartType: 'pie'});    // Create a pie chart
    pieChart.MPChart('setData', data);                                               // Set the chart's data
});

Demo
Code
var data = {
  'US': {
      '2010-09-01': 10,
      '2010-09-02': 11,
      '2010-09-03': 8
  },
  'CH': {
      '2010-09-01': 1,
      '2010-09-02': 18,
      '2010-09-03': 17
  },
  'IN': {
      '2010-09-01': 32,
      '2010-09-02': 37,
      '2010-09-03': 30
  }
}
$(document).ready(function() {
    var mapChart = $('<div></div>').appendTo('body').MPChart({chartType: 'map'});    // Create a map chart
    mapChart.MPChart('setData', data);                                               // Set the chart's data
});

Demo




MPTable $('<div>').MPTable([options]);

Decorates the specified jQuery object with a table for viewing data. Returns a jQuery object.

Options
Name Type Required Description
data Object No data can be an Object of Objects containing String keys mapping numerical values, or it can be a flat Object of String keys mapping to numerical values. See the example code for details.
firstColHeader String No Add a header to the first column, which is blank by default. See the example below for details.
showPercentages Boolean No If showPercentages is true and the table cells contain numeric values, show the difference between horizontally adjacent cells as a percentage. See the example below for details.

Methods
Name Signature Return type Description
setData MPTable('setData', data) undefined Populates the table with data.

Code
$(document).ready(function() {
    // assumes you have an event called 'Button clicked' and a property called '$city'
    MP.api.segment('Button clicked', '$city', {limit: 10}).done(function(results) {
        $('<div></div>').appendTo('body').MPTable({ // create table; try scrolling horizontally over demo below
            data: results,
            showPercentages: true,
            firstColHeader: 'Button clicked / city'
        });
    });
});
Demo