A/B tests are very commonly performed by data analysts and data scientists. It is important that you get some practice working with the difficulties of these
For this project, you will be working to understand the results of an A/B test run by an e-commerce website. Your goal is to work through this notebook to help the company understand if they should implement the new page, keep the old page, or perhaps run the experiment longer to make their decision.
To get started, let's import our libraries.
import pandas as pd
import numpy as np
import random
import matplotlib.pyplot as plt
%matplotlib inline
#We are setting the seed to assure you get the same answers on quizzes as we set up
random.seed(42)
1.
Now, read in the ab_data.csv
data. Store it in df
. Use your dataframe to answer the questions in Quiz 1 of the classroom.
a. Read in the dataset and take a look at the top few rows here:
df = pd.read_csv('ab_data.csv')
df.head()
b. Use the cell below to find the number of rows in the dataset.
df.shape
c. The number of unique users in the dataset.
df.user_id.nunique()
d. The proportion of users converted.
# By finding the mean of converted
df.converted.mean()
e. The number of times the new_page
and treatment
don't match.
# Finding if the group is treatment and landing_page is not new_page vice versa
df.query('(group !="treatment" and landing_page == "new_page") or (group =="treatment" and landing_page != "new_page")') ['user_id'].count()
f. Do any of the rows have missing values?
df.isnull().sum()
2.
For the rows where treatment does not match with new_page or control does not match with old_page, we cannot be sure if this row truly received the new or old page. Use Quiz 2 in the classroom to figure out how we should handle these rows.
a. Now use the answer to the quiz to create a new dataset that meets the specifications from the quiz. Store your new dataframe in df2.
# Drop all that does not match
df2 = df.drop(df.query('(group == "treatment" and landing_page != "new_page") or (group != "treatment" and landing_page == "new_page") or (group == "control" and landing_page != "old_page") or (group != "control" and landing_page == "old_page")').index)
# Check all of the correct rows were dropped
df2[((df2['group'] == 'treatment') == (df2['landing_page'] == 'new_page')) == False].shape[0]
df2.shape
3.
Use df2 and the cells below to answer questions for Quiz3 in the classroom.
a. How many unique user_ids are in df2?
df.user_id.nunique()
b. There is one user_id repeated in df2. What is it?
#Finding the duplicated users
df2[df2.duplicated(['user_id'], keep=False)]['user_id']
c. What is the row information for the repeat user_id?
df2[df2['user_id']== 773192]
d. Remove one of the rows with a duplicate user_id, but keep your dataframe as df2.
#Dropping the duplicated row
df2.drop([2893], inplace=True)
df2[df2['user_id']== 773192]
4.
Use df2 in the cells below to answer the quiz questions related to Quiz 4 in the classroom.
a. What is the probability of an individual converting regardless of the page they receive?
p_converted = df2.converted.mean()
print('Probability is : ' , p_converted)
b. Given that an individual was in the control
group, what is the probability they converted?
p_control= df2.query('group == "control"')['converted'].mean()
print('Probability is : ' ,p_control)
c. Given that an individual was in the treatment
group, what is the probability they converted?
p_treatment= df2.query('group == "treatment"')['converted'].mean()
print('Probability is : ' , p_treatment)
obs_diff = p_treatment - p_control
obs_diff
d. What is the probability that an individual received the new page?
p_new_page= df2.query('landing_page == "new_page"').count() [0]/ df2.shape[0]
print('Probability is : ' , p_new_page)
e. Consider your results from parts (a) through (d) above, and explain below whether you think there is sufficient evidence to conclude that the new treatment page leads to more conversions.
Based on the probabilities, the control group, which is the group with the old page. Converted at a higher rate than the treatment, which is the group with the new page.
As there is not any sufficient evidence to say that the new treatment page leads to more conversions.
Notice that because of the time stamp associated with each event, you could technically run a hypothesis test continuously as each observation was observed.
However, then the hard question is do you stop as soon as one page is considered significantly better than another or does it need to happen consistently for a certain amount of time? How long do you run to render a decision that neither page is better than another?
These questions are the difficult parts associated with A/B tests in general.
1.
For now, consider you need to make the decision just based on all the data provided. If you want to assume that the old page is better unless the new page proves to be definitely better at a Type I error rate of 5%, what should your null and alternative hypotheses be? You can state your hypothesis in terms of words or in terms of $p_{old}$ and $p_{new}$, which are the converted rates for the old and new pages.
2.
Assume under the null hypothesis, $p_{new}$ and $p_{old}$ both have "true" success rates equal to the converted success rate regardless of page - that is $p_{new}$ and $p_{old}$ are equal. Furthermore, assume they are equal to the converted rate in ab_data.csv regardless of the page.
Use a sample size for each page equal to the ones in ab_data.csv.
Perform the sampling distribution for the difference in converted between the two pages over 10,000 iterations of calculating an estimate from the null.
Use the cells below to provide the necessary parts of this simulation. If this doesn't make complete sense right now, don't worry - you are going to work through the problems below to complete this problem. You can use Quiz 5 in the classroom to make sure you are on the right track.
a. What is the conversion rate for $p_{new}$ under the null?
# Calculate probability of conversion for new page
p_new = df2.converted.mean()
print('Probability of conversion for new page :', p_new)
b. What is the conversion rate for $p_{old}$ under the null?
# Calculate probability of conversion for old page
p_old = df2.converted.mean()
print('Probability of conversion for old page :', p_old)
#p_new - p_old under the null
print('The difference between p_new and p_old under the null: ', (p_new-p_old))
c. What is $n_{new}$, the number of individuals in the treatment group?
num_new= df2.query('group == "treatment"')['user_id'].count()
print('The number of individuals in the treatment group:', num_new)
d. What is $n_{old}$, the number of individuals in the control group?
num_old= df2.query('group == "control"')['user_id'].count()
print('The number of individuals in the control group: ', num_old)
e. Simulate $n_{new}$ transactions with a conversion rate of $p_{new}$ under the null. Store these $n_{new}$ 1's and 0's in new_page_converted.
# Simulating num_new transactions with a convert rate of p_new under the null
new_page_converted = np.random.choice([0, 1], num_new, p = [p_new, 1-p_new])
f. Simulate $n_{old}$ transactions with a conversion rate of $p_{old}$ under the null. Store these $n_{old}$ 1's and 0's in old_page_converted.
# Simulating num_old transactions with a convert rate of p_old under the null
old_page_converted = np.random.choice([0, 1], num_old, p = [p_old, 1-p_old])
g. Find $p_{new}$ - $p_{old}$ for your simulated values from part (e) and (f).
#Difference between P_new and P_old
diff_new_old = new_page_converted.mean() - old_page_converted.mean()
print('The difference is :',diff_new_old)
h. Create 10,000 $p_{new}$ - $p_{old}$ values using the same simulation process you used in parts (a) through (g) above. Store all 10,000 values in a NumPy array called p_diffs.
# Creating 10,000 (𝑝_𝑛𝑒𝑤 - 𝑝_𝑜𝑙𝑑) values using the same simulation process
p_diffs = []
for p in range(10000):
new_page_converted = np.random.choice([0, 1], size = num_new, p = [p_new, 1 - p_new])
old_page_converted = np.random.choice([0, 1], size = num_old, p = [p_old, 1 - p_old])
diff_new_old = new_page_converted.mean() - old_page_converted.mean()
p_diffs.append(diff_new_old)
# Storing all 10,000 values in a Numpy array
p_diffs = np.array(p_diffs)
i. Plot a histogram of the p_diffs. Does this plot look like what you expected? Use the matching problem in the classroom to assure you fully understand what was computed here.
# Histogram of p_diffs
plt.hist(p_diffs)
plt.ylabel('# of Simulations')
plt.xlabel('p_diffs')
plt.title('Simulated Difference of New Page and Old Page Converted Under the Null');
plt.axvline(x=obs_diff, color='r');
j. What proportion of the p_diffs are greater than the actual difference observed in ab_data.csv?
prop = (p_diffs > obs_diff).mean()
print('The proportion of the p_diffs are greater than the actual difference = ',prop)
k. Please explain using the vocabulary you've learned in this course what you just computed in part j. What is this value called in scientific studies? What does this value mean in terms of whether or not there is a difference between the new and old pages?
The one which was calculaed in 'j' called P-value. if the P-value less than (<0.5) would be rejecting the null-hypothesis. And more than this would be failed to reject the null-hypothesis. According to our P-value there is no statisical evidence to reject the null-hypothesis since the P-value = 0.905.
l. We could also use a built-in to achieve similar results. Though using the built-in might be easier to code, the above portions are a walkthrough of the ideas that are critical to correctly thinking about statistical significance. Fill in the below to calculate the number of conversions for each page, as well as the number of individuals who received each page. Let n_old and n_new refer the the number of rows associated with the old page and new pages, respectively.
convert_old = df2.query("landing_page == 'old_page' and converted==1").shape[0]
convert_new = df2.query("landing_page == 'new_page' and converted==1").shape[0]
n_old = df2.query("landing_page == 'old_page'").shape[0]
n_new = df2.query("landing_page == 'new_page'").shape[0]
m. Now use stats.proportions_ztest
to compute your test statistic and p-value. Here is a helpful link on using the built in.
# Caculating p-value and z-score
z_score, p_value = sm.stats.proportions_ztest([convert_old, convert_new], [n_old, n_new], alternative = 'smaller')
print('The Z-score is: ', z_score,' and the P-value is: ', p_value)
n. What do the z-score and p-value you computed in the previous question mean for the conversion rates of the old and new pages? Do they agree with the findings in parts j. and k.?
The Z-score is measures of standard deviation, Which indicates if the Z-score value between -1.96 and 1.96, the P-value would be larger than (0.05). It would fail to reject the null hypothesis.
Based on the values, it failed to reject the null-hypothesis
1.
In this final part, you will see that the result you achieved in the A/B test in Part II above can also be achieved by performing regression.
a. Since each row is either a conversion or no conversion, what type of regression should you be performing in this case?
Logistic Regression
b. The goal is to use statsmodels to fit the regression model you specified in part a. to see if there is a significant difference in conversion based on which page a customer receives. However, you first need to create in df2 a column for the intercept, and create a dummy variable column for which page each user received. Add an intercept column, as well as an ab_page column, which is 1 when an individual receives the treatment and 0 if control.
# Intercept column
df2['intercept'] = 1
# Create dummy variable
df2['ab_page'] = pd.get_dummies(df2['group'])['treatment']
df2.head()
c. Use statsmodels to instantiate your regression model on the two columns you created in part b., then fit the model using the two columns you created in part b. to predict whether or not an individual converts.
#Loading Logit regression model
logit_mod = sm.Logit(df2['converted'], df2[['intercept','ab_page']])
result = logit_mod.fit()
d. Provide the summary of your model below, and use it as necessary to answer the following questions.
# Summary of the model
print(result.summary2())
e. What is the p-value associated with ab_page? Why does it differ from the value you found in Part II?
Hint: What are the null and alternative hypotheses associated with your regression model, and how do they compare to the null and alternative hypotheses in Part II?
- The P-value associated with ab_page is 0.1899
In previous section we were trying to reject the null-hypothses but we couldn't because the P-value was 0.905. While in this section we checked if there is a significant difference in conversion based on which page a customer receives. Still tell us there is no that diffrence between old and new page.
f. Now, you are considering other things that might influence whether or not an individual converts. Discuss why it is a good idea to consider other factors to add into your regression model. Are there any disadvantages to adding additional terms into your regression model?
We should consider adding additional factors to the regression models. However, it might influence the result in direction that we don't know.
g. Now along with testing if the conversion rate changes for different pages, also add an effect based on which country a user lives in. You will need to read in the countries.csv dataset and merge together your datasets on the appropriate rows. Here are the docs for joining tables.
Does it appear that country had an impact on conversion? Don't forget to create dummy variables for these country columns - Hint: You will need two columns for the three dummy variables. Provide the statistical output as well as a written response to answer this question.
#Reading the countries
countries=pd.read_csv('countries.csv')
countries.head(3)
# To check how many lebels
countries['country'].unique()
# Mergeing Dataframes
df3= df2.merge(countries, on ='user_id', how='left')
df3.head()
# Create dummy variables
df3[['CA','US', 'UK']] = pd.get_dummies(df3['country'])[['CA','US', "UK"]]
df3.head()
# Create intercept variable
df3['intercept'] = 1
# Loading the Logit regression model for country and converted, and use CA as baseline
Model_Log = sm.Logit(df3['converted'], df3[['intercept','UK','US']])
result2 = Model_Log.fit()
# Print the result
print (result2.summary2())
h. Though you have now looked at the individual factors of country and page on conversion, we would now like to look at an interaction between page and country to see if there significant effects on conversion. Create the necessary additional columns, and fit the new model.
Provide the summary results, and your conclusions based on the results.
# New intereacton variable between page and country ( UK and US )
df3['UK_ab_page'] = df3['UK']*df3['ab_page']
df3['US_ab_page'] = df3['US']*df3['ab_page']
df3.head(3)
# Loading Logit regression model
Model_Log = sm.Logit(df3['converted'], df3[['intercept','UK_ab_page','US_ab_page']])
result3 = Model_Log.fit()
# Print the result
print(result3.summary2())
The results in the summary of UK_ab_page and US_ab_page are larger than 0.05 value, which are not statistically significant.
The intercept's p-value is less than 0.05, which is statistically significant enough for the converted rate.
The country factor is not significant on the converted rate.
All the P-values were more than 0.5. Therefore, we would fail to reject the null hypothesis. Also, there is not any sufficient evidence to suggest that the new page results have more conversions than the old page. Even after adding the countries variable did not lead to higher conversion for the new page.
Reference:
from subprocess import call
call(['python', '-m', 'nbconvert', 'Analyze_ab_test_results_notebook.ipynb'])