Laravel cohort analysis, Eloquent Query for Weekly retention curve chart


At the end of the last winter, I received the following email in my inbox, from one of my follower on techalyst.com, seeking my help to write a Laravel query to export data to create a retention curve chart (Weekly cohorts) from mysql database.

Following is his requirement,

Mission
• Analyses of how members flow through the registration process on our site.
• Get analytics in how the registration process improves over time.
• Get information on where we should improve the registration process; where do members get stuck?

Background of App

system has just optimized the registration flow. After signing up for an account, the member has to complete a series of steps (form) to complete their profile.
The current steps in registration follow are:

1. Make account - 0% (Step 1)
2. activate account - 20% (Step 2)
3. Complete profile information - 40% (Step 3)
4. interested in Jobs? - 50% (Step4)
5. experience in these jobs? - 70% (Step5)
6. are you Parttime worker? - 90% (Step6)
7. waiting for final review - 99% (Step 7 )
8. approval - 100% (Step8)

At the moment we don't know how members are performing in the Registration flow. We want to know where many members get stuck, so we can make improvements. We want to create a retention curve chart that shows how far a group of members (weekly cohorts) came in the registration process.

Assumptions.
• The chart will look like the one below.
• The chart has one line per weekly cohort.
• The vertical axis is the percentage of members who have been or are still in this step.
• The horizontal axis represents the step in the registration process (based on the step percentage).
• On the first step (X=0) 100% of the members are still active, so all charts start at X=0 Y=100%.
• An export of the Members database table with relevant information attached

Help Needed


Laravel function producing JSON data
Represent the data in web frontend in a chart using Highcharts

Following is the snap of the Member On Boarding table in the database.

Image title


expected output in the chart

Image titleImage title

here is my full solution for his requirement,
first i made a Laravel eloquent model for member-oboarding table

<?php

namespace Techalyst\Chart\Models;

use Illuminate\Database\Eloquent\Model;
class Member extends Model
{
    protected $table = 'member_onboarding';
    public $timestamps = false;


}


and the following is my complete solution which return the json data required by the jquery high-chart in the front end,

solution requires a complex query to rotate the data from columns to row,  This type of rotation of data from columns to rows is known as PIVOTING in terms of SQL.  MySQL does not have a native pivot function but we can use an aggregate function with a CASE expression as an alternative to PIVOTING to get the result


public function dataAction(Request $request)
{

$weekly_retention =
    Member::select([
        DB::raw('DATE_ADD(created_at, INTERVAL(2-DAYOFWEEK(created_at)) DAY) AS week_start'),
        DB::raw('CONCAT(YEAR(created_at), "/", WEEK(created_at)) AS week_name'),
        DB::raw('SUM(CASE WHEN onboarding_perentage <= 100 THEN 1 ELSE 0 END) AS Step1'),
        DB::raw('SUM(CASE WHEN onboarding_perentage > 0 AND onboarding_perentage <= 100 THEN 1 ELSE 0 END) Step2'),
        DB::raw('SUM(CASE WHEN onboarding_perentage > 20 AND onboarding_perentage <= 100 THEN 1 ELSE 0 END) Step3'),
        DB::raw('SUM(CASE WHEN onboarding_perentage > 40 AND onboarding_perentage <= 100 THEN 1 ELSE 0 END) Step4'),
        DB::raw('SUM(CASE WHEN onboarding_perentage > 50 AND onboarding_perentage <= 100 THEN 1 ELSE 0 END) Step5'),
        DB::raw('SUM(CASE WHEN onboarding_perentage > 70 AND onboarding_perentage <= 100 THEN 1 ELSE 0 END) Step6'),
        DB::raw('SUM(CASE WHEN onboarding_perentage > 90 AND onboarding_perentage <= 100 THEN 1 ELSE 0 END) Step7'),
        DB::raw('SUM(CASE WHEN onboarding_perentage = 100 THEN 1 ELSE 0 END) Step8')
    ])
    ->groupBy('week_name')
    ->orderBy(DB::raw('YEAR(created_at)'),'ASC')
    ->orderBy(DB::raw('WEEK(created_at)'),'ASC')
    ->get();

    $chartArray ["chart"] = array (
        "type" => "line"
    );
    $chartArray ["title"] = array (
        "text" => "Weekly Retention Curve"
    );
    $chartArray ["credits"] = array (
        "enabled" => false
    );
    $chartArray ["xAxis"] = array (
        "categories" => array ()
    );
    $chartArray ["tooltip"] = array (
        "valueSuffix" => "%"
    );

    $categoryArray = array (
        '0',
        '20',
        '40',
        '50',
        '70',
        '90',
        '99',
        '100'
    );

    $chartArray ["xAxis"] = array (
        "categories" => $categoryArray
    );
    $chartArray ["yAxis"] = array (
        "title" => array (
            "text" => "Total Onboarded"
        ),
        'labels' => array(
            'format' => '{value}%'
        ),
        'min' => '0',
        'max' => '100'
    );


    foreach ($weekly_retention as $week){
        $dataArray = array();

        for($i = 1; $i <= 8; $i++){

            if($i == 1){
                $dataArray[] = 100;
            }else{
                $dataArray[] = round(($week->{"Step".$i}/$week->Step1) * 100);
            }

        }


        $chartArray ["series"] [] = array (
            "name" => $week->week_start,
            "data" => $dataArray
        );
    }

    return response()->json($chartArray)->setEncodingOptions(JSON_NUMERIC_CHECK);


}


and here is the final out put of my solution for the requirement rendered by highchart

Image title


Due to Busy work schedule , I couldn't explain the solution step by step, if you have any question please write it bellow in the comment box and i will respond you.

Written by Akram Wahid 5 years ago

are you looking for a chief cook who can well craft laravel and vuejs, to make some awsome butterscotch,
yes then it is right time for you to look at my profile.

Do you want to write Response or Comment?

You must be a member of techalyst to proceed!

Continue with your Email ? Sign up / log in

Responses

Be the first one to write a response :(

{{ item.member.name }} - {{ item.created_at_human_readable }}

{{ reply.member.name }} - {{ reply.created_at_human_readable }}