Introduction to Personalization and Analytics

Personalization is a powerful feature. It enables you to leverage machine learning to optimize what's shown in the feed. The 5 most common use cases are:

Famous examples of discovery feeds are Instagram's explore section and Pinterest's main feed. Edge rank is used by Facebook and Linkedin. Stream uses 3 data sources for personalization:

1. Feeds & Follows

The best way to understand how feeds and follows work is to try our 5 minute interactive tutorial. We also have extensive documentation which provides detailed information on the API.

2. Analytics

The purpose of analytics is to track which activities a user is looking at and what they are engaging with. Basically, you want to track everything that indicates a user's interest in something. Common examples include:

  • Clicking on a link
  • Likes or comments
  • Sharing an activity
  • Viewing another user's profile page
  • Search terms

The events and data you want to track is often different than what you traditionally track in Google Analytics or Mixpanel. Stream's analytics is designed to run side by side your existing analytics solution.

3. Collections

Collections enable you to sync information to Stream that's not captured by analytics or feeds. Common examples include user profiles and product information.

Tutorial - Instagram style personalization

This tutorial will explain how to build Instagram style personalization using Stream. The underlying tech is similar to that of a Pinterest style feed, Etsy’s e-commerce recommendations, email digests (such as Quora) or YouTube’s content suggestions.

The goal of this tutorial is to build a feed similar to Instagram's 'Explore' feed. The content is based on what type of images you've engaged with in the past as well as graph analysis. For example, if you often click on snowboarding pictures it will learn that you like snowboarding and display related posts.

Step 1: Test Data

Let's get started. As a first step we'll want to insert test data.

# Step 1: Setup the test data
import stream
import requests
client = stream.connect('YOUR_API_KEY', 'API_KEY_SECRET')

activities = requests.get('http://bit.ly/test-activities-gist').json()
for activity in activities:
    activity_response = client.feed('user', 'global').add_activity(activity)
    print activity_response

If you run the above snippet you'll insert 3 activities with the following images and tags:

#tree #snow #winter #landscape
#beach #van #surfing #travel
#dog #puppy #running #labrador

Step 2: Analytics

For this tutorial we'll only track clicks. If you're building a production application you'll want to track any type of event that signals a user's interest. The example below shows how to perform the analytics tracking if a user clicks on the 2nd image. Analytics tracking is normally done client-side, in this case we are using the JS analytics client.

// set the current user
client.setUser({id: 123, alias: 'john'});
// track a click on the 2nd picture
var engagement = {
  // the label for the engagement, ie click, retweet etc.
  'label': 'click',
  // the ID of the content that the user clicked
  'content': {
    'foreign_id': 'picture:2'
  },
  // score between 0 and 100 indicating the importance of this event 
  // IE. a like is typically a more significant indicator than a click
  'score': 15,
  // (optional) the position in a list of activities
  'position': 2
};

client.trackEngagement(engagement);

The example above uses a score of 15. The score is a number between 0 and 100 and indicates the importance of an event. A click is typically less important than a like or a share, so give those events a higher score.

Step 3: Understanding Features

Those clicks give us a hint that the user is interested in that activity. There are many features attached to a given activity. For this example, we'll take a look at the features attached to the 2nd activity:

# Analyze which features activity with foreign id picture:2 has
features = client.personalization.get('analyze_features', foreign_id='picture:2')
print features

Once you've ran the code, ask yourself why the user is interested in this picture. Is it because the picture is about surfing? Maybe it indicates that he/she likes the post since it's created by a friend? One click doesn't give us significant data, but as the user keeps returning to your app, an interest profile starts to form.

Step 4: Follows

Personalization works best if you combine explicit follow relationships with analytics events. The example below shows how to create a follow relationship.

# John follows user conner3400
client.feed('timeline', 'john').follow('user', 'conner3400')

Step 5: Reading the Personalized Feed

With both follows and analytics in place we now have the ability to read the personalized feed.

activities = client.personalization.get('personalized_feed', user_id='john')

Step 6: Follow Suggestions

With the follow relationships in place, we have enough data to create follow suggestions.

suggestions = client.personalization.get('follow_suggestions', user_id='john')

This tutorial explained a simplified version of personalization for your app. Note: these algorithms need to be customized for each enterprise customer. Contact our data science team to learn how personalization can enhance your app.

Personalized feeds

Personalization is custom for every enterprise customer of Stream. The SDK exposes a flexible GET method to enable you to easily make authenticated get requests to personalization:

# Be sure to specify your base url
client.base_url = 'mycompany.getstream.io'
# Read the personalized feed for a given user
client.personalization.get('personalized_feed', user_id=123)
# Our data science team will typically tell you which endpoint to use
client.personalization.get('discovery_feed', user_id=123)

Follow suggestions

Stream makes it easy to add follow suggestions to your app. Simply make the API call shown below to retrieve a list of follow suggestions.

suggestions = client.personalization.get('follow_suggestions', user_id=123)

Follow suggestions are disabled by default. Contact support so we can enable it for your organization.

By default the follow suggestions are based on a standardized graph analysis algorithm. Our data science team can work with you to create a customized algorithm for your app. Contact sales@getstream.io to learn more.

Analytics Clients

We recommend tracking every event for each user, which allows you to gain a better understanding of that user's interests. Common examples include:

  • Clicking on a link
  • Likes or comments
  • Sharing an activity
  • Viewing another user's profile page
  • Searching for a certain user/content/topic/etc.

Stream Analytics supports tracking events via JS, iOS, Android or email redirects:

Note: The events and data you want to track is often different than what you traditionally track in Google Analytics or Mixpanel. Stream's analytics is intended to work beside your existing analytics solution.

Analytics Installation & Setup

You're currently not logged in. Login so we can add your API key in the documentation snippets.


The code below shows how to install Stream's Analytics SDK:

// Add this async loader code anywhere in your HTML code

<script type="text/javascript">
!function(t,e){t("StreamAnalytics","https://d2j1fszo1axgmp.cloudfront.net/2.7.0/stream-analytics.min.js",e)}(function(t,e,n){var s,i,r;n["_"+t]={},n[t]=function(e){n["_"+t].clients=n["_"+t].clients||{},n["_"+t].clients[e.apiKey]=this,this._config=e};var c=function(t){return function(){return this["_"+t]=this["_"+t]||[],this["_"+t].push(arguments),this}};s=["setUser","trackImpression","trackEngagement"];for(var a=0;a<s.length;a++){var o=s[a];n[t].prototype[o]=c(o)}i=document.createElement("script"),i.async=!0,i.src=e,r=document.getElementsByTagName("script")[0],r.parentNode.insertBefore(i,r)},this);
</script>
// the client is available via CocoaPods, just add this to you Podfile

pod 'stream-analytics-ios'
// download the latest release from here: https://github.com/GetStream/stream-analytics-android/releases/

Include the above code snippet in the <head></head> section of your page. Note: the JavaScript is loaded asynchronously for optimal performance.

Using CommonJS modules

If you're using CommonJS modules, run the following command in your project directory:

npm install stream-analytics --save

After installing the package, require it in your app:

var StreamAnalytics = require('stream-analytics');

Client setup

The snippet below shows you how to initialize Stream's analytics client:

var client = new StreamAnalytics({
  apiKey: "YOUR_API_KEY",
  token: "ANALYTICS_TOKEN"
});
// Initialize StreamAnalytics in AppDelegate

#import "AppDelegate.h"
#import "Stream.h"

@interface AppDelegate ()
@end

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    //enable debug logging
    [StreamAnalytics enableLogging:YES];
    return YES;
}
@end
// Import the library in your Gradle project:

dependencies {
  compile files('/path_to_file/stream-analytics-release-1.1.0.aar')
}

// Initialize the StreamAnalytics client
StreamAnalyticsAuth auth = new StreamAnalyticsAuth("YOUR_API_KEY","ANALYTICS_TOKEN");
StreamAnalytics client = StreamAnalyticsImpl.getInstance(auth);

Add your credentials to your project's Info.plist file:


      <dict>
      <key>APIKey</key>
      <string></string>
      <key>JWTToken</key>
      <string></string>
      </dict>
    

Bind the service in your application's manifest.xml:


        <uses-permission android:name="android.permission.INTERNET" />
      

Always specify the current user before sending events:

// user id and a friendly alias for easier to read reports
client.setUser({id: 486892, alias: 'Julian'});
#import "Stream.h"


// set user id only 
[[StreamAnalytics sharedInstance] setUserId:@"486892"];

// add friendly alias for the user
[[StreamAnalytics sharedInstance] setUserId:@"486892" andAlias:@"Julian"];
// set user data
client.setUser("486892", "Eric");

// you can also set the userId only
// client.setUserId("486892");

Tracking Engagements

The snippet below shows an example of how to track engagements. Emgagement examples include likes, comments, profile views and link clicks.

var engagement = {
  // the label for the engagement, ie click, retweet etc.
  'label': 'click',
  // the ID of the content that the user clicked
  'content': {
    'foreign_id': 'tweet:34349698'
  },
  // score between 0 and 100 indicating the importance of this event 
  // IE. a like is typically a more significant indicator than a click
  'score': 2,
  // (optional) the position in a list of activities
  'position': 3,
  // (optional) the feed the user is looking at
  'feed_id': 'user:thierry',
  // (optional) the location in your app. ie email, profile page etc
  'location': 'profile_page'
};

client.trackEngagement(engagement);
StreamEngagement *event = [StreamEngagement createEngagementEvent:@"click" withContent: @{@"foreign_id":@"message:34349698"}.mutableCopy]];

event.position = @"3";
event.boost = @2;
event.feedId = @"user:thierry";
event.location = @"profile_page";

[[StreamAnalytics sharedInstance] send:event];
client.send(new Engagement.EventBuilder()
    .withFeedId("user:thierry")
    .withContent(
        new Content.ContentBuilder()
            .withForeignId("message:34349698")
            .withAttribute("verb", "share")
            .withAttribute("actor", new ContentAttribute("1", "user1"))
            .build()
        )
    .withBoost(2)
    .withLocation("profile_page")
    .withPosition("3")                
    .build()
);

Parameters

Name Type Description Optional
label string The type of event (ie. click, share, search, etc.).
content string or object The content the engagement relates to, either as an ID or content object.
score string A score between 0 and 100 indicating the importance of an event.
position string The placement in a list of activities, starting at 0.
feed_id string The specific feed the user is viewing.
location string The page in your app (ie. email, homepage, profile page, etc.).

Tracking Impressions

Tracking impressions allows you to learn what specific users are not interested in. If the app often shows posts about football, and the user never engages with those posts, we can conclude that we're displaying the wrong content. The code below shows how to track that a user viewed 3 specific activities:

var impression = {
  // the list of content IDs displayed to the user
  'content_list': ['tweet:34349698', 'tweet:34349699', 'tweet:34349697'],
  // (optional) the feed where this content is coming from
  'feed_id': 'flat:tommaso',
  // (optional) the location in your app. ie email, profile page etc
  'location': 'profile_page'
};

// send the impression events
client.trackImpression(impression);
//track an impression
StreamImpression *event = [StreamImpression createImpressionEventWithContentList:@[@"song:34349698", @"song:34349699", @"song:34349697"]];

// (optional) the feed where this content is coming from
event.feedId = @"flat:tommaso";
// (optional) the location in your app. ie email, profile page etc
event.location = @"ios-app";

// send the impression events
[[StreamAnalytics sharedInstance] send:event];
client.send(new Impression.EventBuilder()
    .withContentList(
        new Content.ContentBuilder()
            .withForeignId("tweet:34349698")
            .withAttribute("verb", "share")
            .withAttribute("actor", new ContentAttribute("1", "user1"))
            .build(),
        new Content.ContentBuilder()
            .withForeignId("tweet:34349699")
            .build(),
        new Content.ContentBuilder()
            .withForeignId("tweet:34349610")
            .build()
    )
    .withFeedId("flat:tommaso")
    .withLocation("android-app")
    .build()
);

Be sure to use the same foreign ids as used in your feeds. This allows Stream to understand the content of the activities.

Parameters

Name Type Description Optional
content_list list of strings or objects The list of content the user is looking at. Either a list of IDs or objects.
feed_id string The feed the user is looking at.
location string The location in your app (ie. email, homepage, profile page, etc.).

Email tracking

Users tend to engage with emails, even when they aren't engaged with your app. Thus, it's important to track how they interact with your emails.

Tracking clicks in emails works via redirects. You can use our client libraries to generate a redirect link.

Note: you can only create redirect links from your server-side application using one of Stream's API clients. Refer to our API documentation for more information on how to obtain the clients.

Example

# the url to redirect to
target_url = 'http://mysite.com/detail'

# track the impressions and a click
impression = {
    'content_list': ['tweet:1', 'tweet:2', 'tweet:3'], 
    'user_data': 'tommaso', 
    'location': 'email',
    'feed_id': 'user:global'
}

engagement = {
    'content': 'tweet:2', 
    'label': 'click',
    'position': 1, 
    'user_data': 'tommaso', 
    'location': 'email',
    'feed_id': 
    'user:global'
}

events = [impression, engagement]
tracking_url = client.create_redirect_url(target_url, user_id, events)

# when the user opens the tracking url in their browser gets redirected to the target url
# the events are added to our analytics platform
$client = new GetStream\Stream\Client('YOUR_API_KEY', 'API_KEY_SECRET');

// the url to redirect to
$targetUrl = 'http://my.application.com/page/';

$impression = [
  'content_list' => ['tweet:34349698', 'tweet:34349699', 'tweet:34349697'],
  'feed_id' => 'flat:tommaso',
  'location' => 'profile_page',
  'user_data' => ['id' => 'bubbles'],
  'label' => 'impression',
];

$engagement = [
    'content' => 'tweet:34349698',
    'feed_id' => 'flat:tommaso',
    'location' => 'profile_page',
    'user_data' => ['id' => 'frank'],
    'label' => 'click',
];

$events = [$impression, $engagement];
$trackingUrl = $client->createRedirectUrl($targetUrl, $events);

// when the user opens the tracking url in their browser gets redirected to the target url
// the events are added to our analytics platform

In the code above, when a user clicks the tracking URL, they are re-directed to the specified target URL. During the re-direct, Stream tracks the impressions and engagement events you specified.

Collections

Collections enable you to sync information to Stream that is not captured by analytics or feeds. Examples include user profiles and product information. To sync this data, use the following 3 endpoints:

  • Upsert
  • Delete
  • Select

The example below shows how to update or insert a user profile via the upsert command. You can update a max of 1000 items at once. If you need to update more than 1000 items you'll need to make multiple requests.

# Update the information for user with id 123
client.collections.upsert('user', [{'id': '123', 'username': 'johndoe', 'favorite_color': 'blue'}])

Verify Stream is storing the correct data by using the select endpoint. A similar syntax can be used for removing the data.

# Read the data for user 123
user = client.collections.select('user', [123])
# Delete the record for user 123
response = client.collections.delete('user', [123])