Fork me on GitHub
Shadow


Table of Contents

What?

Too often, information is collected from users but is never applied to your application to understand what type of users are doing what on your site.

Too often, great content may be shared on your site, but your means of determining what is popular or relevant to your users is a challenge.

Enter Shadow, an analytics engine for developers that integrates seamlessly into your current system.

If Google Analytics is an external stats tracker, consider Shadow to be an internal one.

Idea

Shadow is used within your system to keep track of "objects" meta data and users relations to them. An "object" represents anything in your application. For example, an "object" can be a Post, Image, or even a Comment. Imagine if everytime a user visited a post, you tracked the users gender. You'd be able to query that data later to say that "X% of the people that read this post are Male". That is what meta data in Shadow can be used for.

By default, meta data for simple and complex operations are tracked as a count, meaning every time you track the data, a counter will increment. If you prefer to set the value to something else, you may set it as a string or an array.

Relations are the users connection with an object. If you wanted to add a like button for pictures in your app, your object type might be "picture" and your operation would be "unary". Want to incorporate up/downvotes into your comments? With Shadow, all that is only a few lines of code.

Use Cases

Requirements

Terminology

Meta Tracking

Relation Tracking


The Code

Instantiation

Require Shadow in your script, and initialize it. The only required paramater of Shadow is a String. It represents your application namespace as to avoid object collision within your database.

require  'src/Shadow.php';

$shadow = new Shadow( 'MyAppName' );

Auto Tracking

Shadow can automatically track objects for you.

$shadow = new Shadow( 'MyAppName', true );

By calling true in the second parameter during Instantiation, Shadow will execute Shadow->private:globalTracks, a function in which you can fill with your default tracks.

Alternatively, you may pass a function as the second parameter to call your auto tracks:

$shadow = new Shadow( 'MyAppName', function( $shadow ){
    // Shadow Tracks Here
    $shadow->type( 'global' )->item( 'global' )->meta('impressions')->track();
});

Public Functions

All of the functions listed here are meant to be chained together to create what is called a Shadow "build"

type( $itemType ) 
meta( $key, $value=false )
item( $itemID, $timestamp = false )
relation( $type, $user = false, $value = false )
expires( $time )
track()
get( $start = false, $amount=false )

All of the functions below are meant to be called by themselves, and do not chain the shadow object.

clearDataByType( $type )

Usage

Meta Operations

Simple Operations

Simple operations are for tracking a single attribute of an object.

If the second parameter of meta is a string, it will be set as the value rather than keep count.

Tracking

$shadow->type( "post" )
       ->item( 5 )
       ->meta( "impressions" )
       ->track();

In this example, we are tracking "impressions" on Post ID #5. Everytime this is called, a count of impressions will be incremented.

Getting

$shadow->type( "post" )
       ->item( 5 )
       ->meta( "impressions" )
       ->get();

Change the track function to the get function and in this example, the count of impressions will be returned.

Sample Return (if track was called 180 times)

180

Additional Variations

The following variations of data tracking are used for both Simple and Complex operations

Tracking Strings

$shadow->type( "post" )
       ->item( 5 )
       ->meta( "foo", "bar" )
       ->track();

Passing a string as the second parameter to meta will set the data as you'd expect, and will be able to be retrieved as a string.

Tracking Arrays

$shadow->type( "post" )
       ->item( 5 )
       ->meta( "foo", array( 'hello' => 'world!' ) )
       ->track();

Passing an array as the second parameter to meta will set the data as you'd expect, and will be able to be retrieved as an array.

Adding Expiration Dates

$shadow->type( "post" )
       ->item( 5 )
       ->meta( "foo" ),
       ->expires( "3 days" )
       ->track();

The expires function can be chained to set an expiration date on the data being tracked. This can be used for both simple and complex operations. **Note: ** Data may continuously tracked with the expires method chained. This explicitly means that data will be "reset" if data is tracked at a data past the initial "expires" date set on the object.

The acceptable parameters are as follows:

Complex Operations

Complex operations are for keeping a count of a multiple sub-attributes of an object. To define an attribute as a complex one, simply add a slash in the nave with it's respective value following after the key.

Tracking

$shadow->type( "post" )
       ->item( 5 )
       ->meta( "gender/male" )
       ->track();

In this example, we are tracking the users "gender" (specifically, "male") on Post ID #5. If you have the users information, it may be beneficial to record the users meta information and associate it with on object so you can see what types of people are doing what on your website.

Getting single sub-attribute

$shadow->type( "post" )
       ->item( 5 )
       ->meta( "gender/male" )
       ->get();

Change the track function to the get function and in this example, the count of males will be returned.

Sample Return

7

Getting all sub-attributes

$shadow->type( "post" )
       ->item( 5 )
       ->meta( "gender" )
       ->get();

Remove the specified sub-attribute from your attribute function and all of the sub-attributes for "gender" will be returned:

Sample Return

Array (
    [male] => 7,
    [female] => 20
)

In the above example, we can use the tracked information to deduce that there were significantly more females viewing Post ID #5 than there were males.

Relation Operations

Relation Operations are used to track the relationship between a user and an object in a social setting.

Unary Operations

When a user on Facebook "Likes" a post, the form a Unary relationship with that post, a relationship in which there can only be one other state than the default state. The user can either "Like" the post or not.

Tracking

$shadow->type( "post" )
       ->item( 5 )
       ->relation( "unary", 80, true )
       ->track();

The relation function takes in three parameters, a relation type, the user ID, and the value for the relation.

Unary Relations may only have a value of True or False. If True, a relation will be formed if one does not exist. If False, the relation will be destroyed.

In the example above, this will store a relation in our database that User #80 "Likes" Post #5.

Getting Users Relation to Unary Object

$shadow->type( "post" )
       ->item( 5 )
       ->relation( "unary", 80 )
       ->get();

Change the track function to the get function, and omit the third parameter $value from the relation function and the users relation to the object will be returned.

Sample Return (if the User #80 "Liked" Post #5)

true

Getting Unary Objects Social Value

$shadow->type( "post" )
       ->item( 5 )
       ->relation( "unary" )
       ->get();

If you now omit the second parameter $userID from the relation function ange get the data, an int will be returned representing the number of "Likes" Post #5 has.

Sample Return (15 likes)

15

Getting Popular Unary Objects

$shadow->type( "post" )
       ->relation( "unary" )
       ->get();

If you now omit the item function (which specifies a specific object), a list of the popular $itemType's ("post" in this case) will be returned.

Sample Return: 3 "Posts", stored in the objects array. id represents the Posts ID, count is the "number of likes", and rank is the Social Rank of objects of type "post" that have a Unary relation

Array
(
    [count] => 3
    [type] => post
    [objects] => Array
        (
            [0] => Array
                (
                    [id] => 4
                    [count] => 20
                    [rank] => 1.69941166289984
                )

            [1] => Array
                (
                    [id] => 7
                    [count] => 38
                    [rank] => 1.37037037037037
                )

            [2] => Array
                (
                    [id] => 1
                    [count] => 8
                    [rank] => 0.875
                )

        )

)

Binary Operations

When a user "upvotes" or "downvotes" a submission on Reddit, the user forms a Binary relationship with that submission, a relationship in which there can only be one of two states other than the default state. The user can either "Upvote" or "Downvote" the post. In the following examples, we will use "Comments" as a $postType.

Tracking

$shadow->type( "comment" )
       ->item( 10 )
       ->relation( "binary", 80, true )
       ->track();

The relation function's first parameter is now "binary", and it's value is True.

Binary Relations Values will only accept True, False, or null. If True, a "positive" relation will be formed between the user and the object. If False, a "negative" relation will be formed. If null, any type of relation (if any) will be destroyed.

In the example above, this will store a relation in our database that User #80 "Upvoted" Comment #10. If the third parameter in relation were false, User #80 would have "Downvoted" Comment #10;

Getting Users Relation to Binary Object

$shadow->type( "comment" )
       ->item( 10 )
       ->relation( "binary", 80 )
       ->get();

Change the track function to the get function, and omit the third parameter $value from the relation function and the users relation to the object will be returned.

Sample Return (if the User #80 "Downvoted" Comment #10)

false

Sample Return (if the User #80 "Upvoted" Comment #10)

true

Sample Return (if the User #80 has no relation with Comment #10)

null

Getting Binary Objects Social Value

$shadow->type( "comment" )
       ->item( 10 )
       ->relation( "binary" )
       ->get();

If you now omit the second parameter $userID from the relation function ange get the data, an array of positive and negative votes will be shown for Comment # 10

Sample Return (9 "Upvotes" and 3 "Downvotes")

Array
(
    [positive] => 9,
    [negative] => 3
)

Getting Popular Binary Objects

$shadow->type( "comment" )
       ->relation( "binary" )
       ->get();

If you now omit the item function (which specifies a specific object), a list of the popular $itemType's ("comment" in this case) will be returned.

Sample Return: 3 "Comments", stored in the objects array. id represents the Posts ID, positive is the number of "upvotes", negative is the number of "downvotes" and rank is the Social Rank of objects of type "comment" that have a Binary relation

Array
(
    [count] => 3
    [type] => comment
    [objects] => Array
        (
            [0] => Array
                (
                    [id] => object-idfs
                    [positive] => 14
                    [negative] => 3
                    [rank] => 0.366543291473893
                )

            [1] => Array
                (
                    [id] => object-id
                    [positive] => 14
                    [negative] => 7
                    [rank] => 0.2845286548008661
                )

            [3] => Array
                (
                    [id] => object-idfs
                    [positive] => 21
                    [negative] => 94
                    [rank] => 0.1965433291273893
                )
        )
)

Multary Operations

When a user rates a movie on IMDB, the user forms a Multary relationship with that movie, a relationship in which there can be more than two states other than the default state. The user can "rate" a movie as 3, 7, or even 20. In the following examples, we will use "Movies" as a $postType.

Tracking

$shadow->type( "movie" )
       ->item( 15 )
       ->relation( "multary", 80, 3 )
       ->track();

The relation function's first parameter is now "multary", and it's value is 3.

Multary Relations Values will only accept an Int, false, or null. If the value is an Int, a relation will be formed between the user and object with the Int as the value for the "rating". If false or null, any type of relation (if any) will be destroyed.

In the example above, this will store a relation in our database that User #80 "rated" Movie #15 as 3.

Getting Users Relation to Multary Object

$shadow->type( "movie" )
       ->item( 15 )
       ->relation( "multary", 80 )
       ->get();

Change the track function to the get function, and omit the third parameter $value from the relation function and the users relation to the object will be returned.

Sample Return (if the User #80 "voted" a 3 on Movie #15)

3

Sample Return (if the User #80 has no relation with Movie #15)

null

Getting Multary Objects Social Value

$shadow->type( "movie" )
       ->item( 15 )
       ->relation( "multary" )
       ->get();

If you now omit the second parameter $userID from the relation function and get the data, an int will be returned representing the ratings Movie #15 has

Sample Return (81 "votes" totaling up to 253 with an average vote of 3)

Array
(
    [num_votes] => 81
    [total_votes_count] => 253
    [avg_vote] => 0.3201
)

Getting Popular Multary Objects

$shadow->type( "comment" )
       ->relation( "multary" )
       ->get();

If you now omit the item function (which specifies a specific object), a list of the popular $itemType's ("movie" in this case) will be returned.

Sample Return: 3 "Movies", stored in the objects array. id represents the Movies ID, num_votes is the number of "votes" casted, total_votes_count is the sum of all votes, avg_vote is the average vote across "movie" objects, and rank is the Social Rank of objects of type "movie" that have a Multary relation

Array
(
    [count] => 3
    [type] => movie
    [objects] => Array
        (
            [0] => Array
                (
                    [id] => 8
                    [num_votes] => 13
                    [total_votes_count] => 40
                    [avg_vote] => 0.325
                    [rank] => 0.316240517139895
                )

            [1] => Array
                (
                    [id] => 4
                    [num_votes] => 4
                    [total_votes_count] => 13
                    [avg_vote] => 0.30769230769231
                    [rank] => 0.307698224688894
                )

            [2] => Array
                (
                    [id] => 6
                    [num_votes] => 23
                    [total_votes_count] => 75
                    [avg_vote] => 0.30666666666667
                    [rank] => 0.307045870537496
                )
        )
)

Popularity Algorithms

Unary Algorithm: Custom

Popularity = (likes – 1) / (time_in_hrs + 2)^1.5

This algorithm is derived from Y Combinator's Hacker News.


Binary Algorithm: Lower bound of Wilson Score Confidence Interval for a Bernoulli Parameter


Multary Algorithm: Bayesian Average

Determines the Bayesian Average, the mean of a population with data from the populations being used as a way to minimize deviations/randomness