forked from DataExpert-io/data-engineer-handbook
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request DataExpert-io#227 from DataExpert-io/feature-kpis-…
…experimentation adding kpis and experimentation
- Loading branch information
Showing
4 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# SPOTIFY’S KPI AND EXPERIMENTATION | ||
I am going to discuss about Spotify. I have used Spotify for 4 years. I am a big music fan, and I cannot live my life without music. Back when I just started using such app, Apple music and Spotify were the two biggest music streaming apps. But I am an Android user, so by default, Spotify is my option. However, I would choose Spotify either way because most of my friends use Spotify, I can connect to my network easier. Spotify UI is more pleasing to the eyes and it has better recommendations on music than Apple Music. I discovered so many good songs from their recommendation systems. They also got me thrilled everytime they released the end-of-year music recap. | ||
Now, I also discover Youtube music and I like it as well since it has a lot of older and international songs. Youtube is also really good while partnering with other service such as phone carrier Google Fi, Spectrum. Spotify so far only has student plan and Hulu promotion. In my opinion, Spotify might face a market share loss to Youtube Music in the future. | ||
For this reason, I am going to run 3 experiments to keep me interested in Spotify and loyal as a user. | ||
## Experiment 1: Spotify's Gym Combo vs. Hulu Deal - A Revenue Sign-Up Experiment | ||
**Objective:** Users love combo deals, therefore Spotify is launching some deals to lure there perspective users to sign up. Currently, Spotify is having deal with Hulu. However, that is not a good combo. Since people go to the gym usually listen to beats, Spotify offers discounted deal for Spotify premium and Blink monthly membership. The goal is to see if Blink combo performs better than Hulu one in terms of sign-up revenue. | ||
|
||
**Null Hypothesis:** Blink combo does not perform better than Hulu one in terms of sign-up revenue. | ||
|
||
**Alternative Hypothesis:** Blink combo performs better than Hulu one in terms of sign-up revenue. | ||
|
||
**Leading Metric:** Number of users who sign up after launching the Blink combo. | ||
|
||
**Lagging Metric:** Increase in sign-up sale. | ||
|
||
**Test cell allocation:** 50%-50% | ||
## Experiment 2: Friend Podcast Listening and User Engagement Tracking | ||
**Objective:** Personally, I am curious about what podcast my friends are listening to and I wish there was a podcast viewing feature. I think it could affect the podcast listeners engagement. The goal is to determine whether allowing users to view what podcasts their friends are listening to positively impacts user engagement with podcasts on the platform. | ||
|
||
**Null Hypothesis:** There is no significant difference in user engagement with podcasts between users who can view what podcasts their friends are listening to and users who cannot. | ||
|
||
**Alternative Hypothesis:** There is significant difference in user engagement with podcasts between users who can view what podcasts their friends are listening to and users who cannot. | ||
|
||
**Leading metrics:** An increase in the frequency of users checking this feature might predict higher engagement with podcasts in the future. | ||
|
||
**Lagging metrics:** The percentage of users who continue to use the platform and engage with podcasts over a specified period. | ||
|
||
**Test cell allocation:** 50%-50% | ||
## Experiment 3: Changing Sign-Up Ads to Enhance User Engagement | ||
**Objective:** To determine whether changing the sign-up ad from "Try 3 months free" to "Your friend is listening to this song, wanna hear it" positively impacts user sign-up rates and engagement on the platform. | ||
|
||
**Null Hypothesis:** There is no significant difference in user sign-up rates between the "Try 3 months free" and "Your friend is listening to this song, wanna hear it" ad variations. | ||
|
||
**Alternative Hypothesis:** Users who see the "Your friend is listening to this song, wanna hear it" ad will have a significantly higher sign-up rate compared to those who see the "Try 3 months free" ad. | ||
|
||
**Leading metrics:** the amount of time users spend on Spotify after signing up. | ||
|
||
**Lagging metrics:** the percentage of users who convert from free to premium subscribers after signing up. | ||
|
||
**Test cell allocation:** 50%-50% | ||
|
||
|
||
|
9 changes: 9 additions & 0 deletions
9
bootcamp/materials/5-kpis-and-experimentation/homework/homework.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Homework | ||
|
||
- Pick a product that you love using (spotify, linkedin, etc) | ||
- Describe the user journey of the things you loved about it from the moment you started using it to your use of it now | ||
- Describe 3 experiments you would like to run on this product to see if it would improve the experience | ||
- You should detail out the allocation of each test cell and the different conditions you’re testing | ||
- Your hypothesis of which leading and lagging metrics would be impacted by this experiment | ||
|
||
- Put these files in Markdown files and submit [here](https://bootcamp.techcreator.io/assignments) |
4 changes: 4 additions & 0 deletions
4
bootcamp/materials/5-kpis-and-experimentation/requirements.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Flask | ||
request | ||
jsonify | ||
statsig |
141 changes: 141 additions & 0 deletions
141
bootcamp/materials/5-kpis-and-experimentation/src/server.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
from flask import Flask, jsonify, request | ||
from statsig import statsig | ||
from statsig.statsig_event import StatsigEvent | ||
from statsig.statsig_user import StatsigUser | ||
import random | ||
import os | ||
|
||
API_KEY = os.environ.get('STATSIG_API_KEY') | ||
statsig.initialize(API_KEY) | ||
app = Flask(__name__) | ||
|
||
# Sample in-memory database | ||
tasks = [ | ||
{ | ||
'id': 1, | ||
'title': 'Do the dishes', | ||
'description': 'Odd Tasks', | ||
'done': False | ||
}, | ||
{ | ||
'id': 2, | ||
'title': 'Study for exam', | ||
'description': 'Even Tasks', | ||
'done': False | ||
} | ||
] | ||
|
||
|
||
@app.route('/') | ||
def hello(): | ||
return "Hello, this is a Flask API!" | ||
|
||
|
||
@app.route('/signup') | ||
def signup(): | ||
random_num = request.args.get('random') | ||
hash_string = request.remote_addr | ||
if random_num: | ||
hash_string = str(random.randint(0, 1000000)) | ||
user_id = str(hash(hash_string)) | ||
statsig_user = StatsigUser(user_id) | ||
statsig_event = StatsigEvent( | ||
user=statsig_user, | ||
event_name='visited_signup' | ||
) | ||
statsig.log_event(statsig_event) | ||
return "This is the signup page" | ||
|
||
|
||
@app.route('/tasks', methods=['GET']) | ||
def get_tasks(): | ||
random_num = request.args.get('random') | ||
hash_string = request.remote_addr | ||
if random_num: | ||
hash_string = str(random.randint(0, 1000000)) | ||
user_id = str(hash(hash_string)) | ||
color = statsig.get_experiment(StatsigUser(user_id), "button_color_v3").get("Button Color", "blue") | ||
paragraph_text = statsig.get_experiment(StatsigUser(user_id), "button_color_v3").get("Paragraph Text", "Data Engineering Boot Camp") | ||
experiment_description = 'odd tasks for blue and green, even for red and orange' | ||
filtered_tasks = ''.join(map(lambda a: f""" | ||
<tr> | ||
<td> | ||
{a['id']} | ||
</td> | ||
<td> | ||
{a['title']} | ||
</td> | ||
<td> | ||
{a['description']} | ||
</td> | ||
<td> | ||
{a['done']} | ||
</td> | ||
</tr> | ||
""", list(filter(lambda x: x['id'] % 2 == (0 if color == 'Red' or color == 'Orange' else 1), tasks)))) | ||
return f""" | ||
<div style="{"background: " + color}"> | ||
<h1>{"experiment group: " + color}</h1> | ||
<h2>{"experiment description: " + experiment_description}</h2> | ||
<h5>{"current user identifier is: <i>" + user_id}</i></h5> | ||
<h5>{paragraph_text}</h5> | ||
<table> | ||
<thead> | ||
<th>Id</th> | ||
<th>Title</th> | ||
<th>Description</th> | ||
<th>Done</th> | ||
</thead> | ||
<tbody> | ||
{filtered_tasks} | ||
</tbody> | ||
</table> | ||
<a href="/signup">Go to Signup</a> | ||
</div> | ||
""" | ||
|
||
@app.route('/tasks/<int:task_id>', methods=['GET']) | ||
def get_task(task_id): | ||
task = next((task for task in tasks if task['id'] == task_id), None) | ||
if task: | ||
return jsonify({'task': task}) | ||
return jsonify({'error': 'Task not found'}), 404 | ||
|
||
|
||
@app.route('/tasks', methods=['POST']) | ||
def create_task(): | ||
if not request.json or not 'title' in request.json: | ||
return jsonify({'error': 'The new task must have a title'}), 400 | ||
task = { | ||
'id': tasks[-1]['id'] + 1 if tasks else 1, | ||
'title': request.json['title'], | ||
'description': request.json.get('description', ""), | ||
'done': False | ||
} | ||
tasks.append(task) | ||
return jsonify({'task': task}), 201 | ||
|
||
|
||
@app.route('/tasks/<int:task_id>', methods=['PUT']) | ||
def update_task(task_id): | ||
task = next((task for task in tasks if task['id'] == task_id), None) | ||
if not task: | ||
return jsonify({'error': 'Task not found'}), 404 | ||
if not request.json: | ||
return jsonify({'error': 'Malformed request'}), 400 | ||
task['title'] = request.json.get('title', task['title']) | ||
task['description'] = request.json.get('description', task['description']) | ||
task['done'] = request.json.get('done', task['done']) | ||
return jsonify({'task': task}) | ||
|
||
|
||
@app.route('/tasks/<int:task_id>', methods=['DELETE']) | ||
def delete_task(task_id): | ||
global tasks | ||
tasks = [task for task in tasks if task['id'] != task_id] | ||
return jsonify({'result': True}) | ||
|
||
|
||
if __name__ == '__main__': | ||
app.run(debug=True) |