Customer Demographics Analysis Report
Customer Demographics Analysis Report
1 import pandas as pd
2 import numpy as np
3 import [Link] as plt
4 import seaborn as sns
5 import warnings
6 [Link]('ignore')
In [2]:
1 df=pd.read_csv("customer_data.csv")
In [3]:
1 [Link]()
Out[3]:
5 rows × 29 columns
In [4]:
1 [Link]
Out[4]:
EDA
In [5]:
In [6]:
1 [Link]()
Out[6]:
5 rows × 32 columns
In [7]:
2 or 3 variables of data are from year 1900 or earlier it can be result of wrong entry or an outlier.
In [8]:
1 #Types Of Education
2 [Link](figsize=(10,6))
3 [Link](df["Education"].value_counts(),autopct="%1.1f%%",labels=[Link](df["Education"])
4 [Link]("Distribution Of Education")
5 [Link](title="Classes", loc="center left", bbox_to_anchor=(1, 0.5))
6 [Link]("equal")
7 [Link]()
[Link] ---16.5%
[Link] ---21.7%
[Link] ---9.1%
[Link] ---2.4%
v.2n Cycle ---50.3%
In [29]:
[Link] Income generated is from Graduation but it can be wrong entry or outlier as well.
In [31]:
In [32]:
1 #Distribution Of Kidhome
2 [Link](figsize=(10,6))
3 [Link](df["Kidhome"].value_counts(),autopct="%1.1f%%",labels=[Link](df["Kidhome"]),sha
4 [Link]("Distribution Of Kidhome")
5 [Link](title="Classes", loc="center left", bbox_to_anchor=(1, 0.5))
6 [Link]("equal")
7 [Link]()
1.50.7% of people do not have kids,40.1% people have 1 child whereas 2.1% people have 2childs.
In [33]:
1 #Distribution Of Teenhome
2 [Link](figsize=(10,6))
3 [Link](df["Teenhome"].value_counts(),autopct="%1.1f%%",labels=[Link](df["Teenhome"]),s
4 [Link]("Distribution Of Teenhome")
5 [Link](title="Classes", loc="center left", bbox_to_anchor=(1, 0.5))
6 [Link]("equal")
7 [Link]()
1.51.7% of people do not have teens at their home,46.0% of people have 1 teen wheras 2.3% of people have 2
teens at home.
In [34]:
1 #Recency
2 [Link](figsize=(19,10))
3 [Link](x=df["Recency"].unique(),y=df["Recency"].value_counts(),color="darkblue")
4 [Link]("Recency frequency")
5 [Link]()
In [35]:
In [37]:
In [38]:
In [39]:
[Link] of the registrations are from year 2013 in dataset provided of 2 years.
In [41]:
1 #Distribution Of MntMeatProducts
2 [Link](figsize=(10,5))
3 [Link](df["MntMeatProducts"].unique(),color="turquoise")
4 [Link]("Distribution Of MntMeatProducts")
5 [Link]()
In [42]:
In [43]:
In [45]:
1 #Distribution Of MntMeatProducts
2 [Link](figsize=(10,5))
3 [Link](df["MntFishProducts"].unique(),color="darkblue")
4 [Link]("Distribution Of MntFishProducts")
5 [Link]()
In [46]:
1 #Distribution Of MntSweetProducts
2 [Link](figsize=(10,5))
3 [Link](df["MntSweetProducts"].unique(),color="red")
4 [Link]("Distribution Of MntSweetProducts")
5 [Link]()
In [47]:
In [49]:
1 #Distribution Of MntGoldProds
2 [Link](figsize=(10,5))
3 [Link](df["MntGoldProds"].unique(),color="gold")
4 [Link]("Distribution Of MntGoldProds")
5 [Link]()
In [50]:
In [51]:
In [53]:
[Link] having 2 kids are spending less on gold comapred with having 1 kid or none.
3..Single & married are spending more on gold than comapred to others.
In [55]:
1 df["NumDealsPurchases"].values
Out[55]:
1 #NumDealsPurchases
2 [Link](figsize=(10,5))
3 [Link](x=df["NumDealsPurchases"],palette="husl")
4 [Link]("count")
5 [Link]("Distribution of NumDealsPurchases")
6 [Link]()
[Link] Like to purchase when discount is there can be clearly seen that when discount was there many people
have brought the items.
In [57]:
1 [Link](df["Complain"],color="lightslategrey")
2 [Link]("Complaints")
3 [Link]()
[Link] are not much complaints of customer only 21 complaints are there
In [59]:
1 color = "cornflowerblue"
2
3 fig, axs = [Link](nrows=2, ncols=2, figsize=(17,10))
4
5 axs[0, 0].hist(df["AcceptedCmp1"], color=color)
6 axs[0, 0].set_title("Campaign 1", fontsize=14)
7 axs[0, 0].set_xlabel("Accepted Campaign 1", fontsize=12)
8 axs[0, 0].set_ylabel("Count", fontsize=12)
9
10 axs[0, 1].hist(df["AcceptedCmp2"], color=color)
11 axs[0, 1].set_title("Campaign 2", fontsize=14)
12 axs[0, 1].set_xlabel("Accepted Campaign 2", fontsize=12)
13 axs[0, 1].set_ylabel("Count", fontsize=12)
14
15 axs[1, 0].hist(df["AcceptedCmp3"], color=color)
16 axs[1, 0].set_title("Campaign 3", fontsize=14)
17 axs[1, 0].set_xlabel("Accepted Campaign 3", fontsize=12)
18 axs[1, 0].set_ylabel("Count", fontsize=12)
19
20 axs[1, 1].hist(df["AcceptedCmp4"], color=color)
21 axs[1, 1].set_title("Campaign 4", fontsize=14)
22 axs[1, 1].set_xlabel("Accepted Campaign 4", fontsize=12)
23 axs[1, 1].set_ylabel("Count", fontsize=12)
24
25 fig.subplots_adjust(hspace=0.4, wspace=0.2)
26 [Link](labels=['Responses'], loc='upper right', fontsize=12)
27 [Link]("Campaign Responses")
28 [Link]()
In [61]:
[Link] can be clearly seen people doesnt accept the offer in 2nd campaign ,the ratio is almost same in other 3
campaigns.
[Link] majority of people have not accepted offer in last campaign [Link] are least affected by campaigns.
In [62]:
In [64]:
[Link] purchases are mostly done in majority by single followed by married persons.
[Link] purchases are done equally by people on marital status those are alone,absurd,YOLO are not in large
number anyways.
1 #Education VS NumWebPurchases
2 [Link](figsize=(10,5))
3 [Link](df["Education"],df["NumWebPurchases"],color="lightcoral",edgecolor="black")
4 [Link]("Education")
5 [Link]("NumWebPurchases")
6 [Link]("Education VS NumWebPurchases")
7 [Link]()
In [67]:
1 #Education VS NumStorePurchases
2 [Link](figsize=(10,5))
3 [Link](df["Education"],df["NumStorePurchases"],color="mediumorchid",edgecolor="black")
4 [Link]("Number of Store Purchases")
5 [Link]("Education")
6 [Link]("#Education VS NumStorePurchases")
7 [Link]()
In [68]:
1 #Education VS NumCatalogPurchases
2 [Link](figsize=(10,5))
3 [Link](df["Education"], df["NumCatalogPurchases"], color="olivedrab",edgecolor="black")
4 [Link]("NumCatalogPurchases")
5 [Link]("Education")
6 [Link]("Education VS NumCatalogPurchases")
7 [Link]()
[Link] with all sort of education visit store but the [Link] people with basic are less.
In [69]:
1 #NumWebVisitsMonth
In [70]:
1 #Education vs NumWebVisitsMonth
2 [Link](figsize=(10,5))
3 [Link](x=df["Education"],y=df["NumWebVisitsMonth"],hue=df["NumWebVisitsMonth"],pale
4 [Link]("Education vs NumWebVisitsMonth")
5 [Link]()
In [71]:
1 #Teenhome vs NumWebVisitsMonth
2
3 [Link](figsize=(10,5))
4 [Link](x=df["Teenhome"],y=df["NumWebVisitsMonth"],hue=df["NumWebVisitsMonth"],palet
5 [Link]("Teenhome vs NumWebVisitsMonth")
6 [Link]()
In [72]:
1 #Marital_Status vs NumWebVisitsMonth
2 [Link](figsize=(15,8))
3
4 [Link](x=df["Marital_Status"],y=df["NumWebVisitsMonth"],hue=df["NumWebVisitsMonth"
5 [Link]("Marital_Status vs NumWebVisitsMonth")
6 [Link]()
In [73]:
1 # Kidhome vs NumWebVisitsMonth
2 [Link](figsize=(10,5))
3
4 [Link](data=df,x="Kidhome",y="NumWebVisitsMonth",hue=df["NumWebVisitsMonth"],palett
5 [Link]("Kidhome vs NumWebVisitsMonth")
6 [Link]()
In [74]:
1 # Income vs NumWebVisitsMonth
2 [Link](figsize=(10,5))
3
4 [Link](data=df,x="Income",y="NumWebVisitsMonth",palette="cool",hue=df["NumWebVisi
5 [Link]("Income vs NumWebVisitsMonth")
6 [Link]()
[Link] sort of people visit website but [Link] people with basic education are less visting comparitively to others.
[Link] trying to find if [Link] visits depends on Teenhome & Kidhome but that was not the case,No sort of relation
found between them.
[Link] Status is also a not a consideration for visiting website people with all sort of marital status visit it,count of
widows and others is less because we have very small [Link] data regarding to them in the dataset.
[Link] with all sort of income are visiting website frequently no pattern identified on one class.
In [75]:
Conclusions/Observations:
Birth Year
[Link] is of people born between 1940-2000.
2.2 or 3 variables of data are from year 1900 or earlier it can be result of wrong entry or an outlier.
Education:
[Link] is divided into 5 types:
[Link] ---16.5%
[Link] ---21.7%
[Link] ---9.1%
[Link] ---2.4%
v.2n Cycle ---50.3%
Marital Status:
[Link] Status is divided into 8 types:
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
iii O O
Income:
[Link] of Basic education is less comapred to others.
[Link] Income generated is from Graduation but it is too high,can be wrong entry or outlier as well.
[Link] year wise income is distributed equally but some group of people from birth year 1970-1980 are earning
more compared to others.
2.51.7% of people do not have teens at their home,46.0% of people have 1 teen wheras 2.3% of people have 2
teens at home.
Amount Spend:
[Link]/Highest amount is spend on wine.
[Link] with basic education are not spending any amount on wines.
[Link] with basic education are not spending much on fruits & meat as comapared to others
[Link] types of people differentiated on basis of martial status are spending nearby same on fruits& meat products.
Registrations:
Most of the registrations are from year 2013 in dataset provided of 2 years.
Complaints:
There are not much complaints of customer only 21 complaints are there
[Link] having 2 kids are spending less on gold comapred with having 1 kid or none.
3..Single & married are spending more on gold than comapred to others.
Offer Campaigns:
[Link] can be clearly seen people doesnt accept the offer in 2nd campaign ,the ratio is almost same in other 3
campaigns.
[Link] majority of people have not accepted offer in last campaign [Link] are least affected by campaigns.
Purchases:
[Link] purchases are mostly done in majority by single followed by married persons.
[Link] purchases are done equally by people on marital status those are alone,absurd,YOLO are not in large
number anyways.
[Link] trying to find if [Link] visits depends on Teenhome & Kidhome but that was not the case,No sort of relation
found between them.
[Link] Status is also a not a consideration for visiting website people with all sort of marital status visit it,count of
widows and others is less because we have very small [Link] data regarding to them in the dataset.
[Link] with all sort of education visit store but the [Link] people with basic are less.
[Link] with all sort of income are visiting website frequently no pattern identified on one class.
DATA PREPROCESSING
In [76]:
In [77]:
1 [Link](["ID","Date_enroll","Month_enroll","Year_enroll","Dt_Customer"],axis=1,inplace=Tru
In [78]:
Out[78]:
182
In [79]:
1 #Dropping Duplicates
2 df.drop_duplicates(inplace=True)
In [80]:
1 [Link]().sum()
Out[80]:
In [81]:
In [82]:
Out[82]:
Year_Birth 0
Education 0
Marital_Status 0
Income 24
Kidhome 0
Teenhome 0
Recency 0
MntWines 0
MntFruits 0
MntMeatProducts 0
MntFishProducts 0
MntSweetProducts 0
MntGoldProds 0
NumDealsPurchases 0
NumWebPurchases 0
NumCatalogPurchases 0
NumStorePurchases 0
NumWebVisitsMonth 0
AcceptedCmp3 0
AcceptedCmp4 0
AcceptedCmp5 0
AcceptedCmp1 0
AcceptedCmp2 0
Complain 0
Z_CostContact 0
Z_Revenue 0
Response 0
dtype: int64
In [83]:
In [84]:
1 [Link]().sum()
Out[84]:
Year_Birth 0
Education 0
Marital_Status 0
Income 0
Kidhome 0
Teenhome 0
Recency 0
MntWines 0
MntFruits 0
MntMeatProducts 0
MntFishProducts 0
MntSweetProducts 0
MntGoldProds 0
NumDealsPurchases 0
NumWebPurchases 0
NumCatalogPurchases 0
NumStorePurchases 0
NumWebVisitsMonth 0
AcceptedCmp3 0
AcceptedCmp4 0
AcceptedCmp5 0
AcceptedCmp1 0
AcceptedCmp2 0
Complain 0
Z_CostContact 0
Z_Revenue 0
Response 0
dtype: int64
In [85]:
1 [Link]()
Out[85]:
8 rows × 25 columns
In [86]:
1 [Link]()
Out[86]:
5 rows × 27 columns
In [87]:
1 df_categoric.head()
Out[88]:
Education Marital_Status
0 Graduation Single
1 Graduation Single
2 Graduation Together
3 Graduation Together
4 PhD Married
In [89]:
1 #Encoding
In [90]:
1 enc_categoric=pd.get_dummies(df_categoric)
2 enc_categoric.head()
Out[90]:
Education_2n
Education_Basic Education_Graduation Education_Master Education_PhD Marital_
Cycle
0 0 0 1 0 0
1 0 0 1 0 0
2 0 0 1 0 0
3 0 0 1 0 0
4 0 0 0 0 1
In [91]:
In [92]:
1 final_df=[Link]([enc_categoric,df_Numeric],axis=1)
In [93]:
1 final_df.head()
Out[93]:
Education_2n
Education_Basic Education_Graduation Education_Master Education_PhD Marital_
Cycle
0 0 0 1 0 0
1 0 0 1 0 0
2 0 0 1 0 0
3 0 0 1 0 0
4 0 0 0 0 1
5 rows × 37 columns
In [94]:
1 final_df.columns
Out[94]:
PCA
In [137]:
In [138]:
1 scaled_df.shape
Out[138]:
(2034, 37)
In [139]:
1 [Link]()
Out[139]:
Education_2n
Education_Basic Education_Graduation Education_Master Education_PhD Marital_
Cycle
5 rows × 37 columns
In [140]:
In [141]:
1 pca=PCA(n_components=37)
2 pca.fit_transform(df2)
Out[141]:
Out[142]:
In [154]:
1 [Link](figsize=(10,5))
2 [Link]([Link](pca.explained_variance_ratio_),linewidth=4,markersize=10,color="blue")
3 [Link]('Number of Components')
4 [Link]('Explained Variance Ratio')
5 [Link]()
In [163]:
1 [Link](figsize=(10,5))
2 [Link]([Link](pca.explained_variance_ratio_),color='blue', linewidth=4,)
3 [Link](y=0.9, color='cyan', linestyle='--', label='90% EVR')
4 [Link](x=23, color='cyan', linestyle='--', label='Selected Components')
5 [Link](23,0.9,marker="*",linewidths=10,color="red")
6 [Link]('PCA - Explained Variance Ratio')
7 [Link]('Number of Components')
8 [Link]('Explained Variance Ratio')
9 [Link]([Link](0, 1.1, step=0.1))
10 [Link](3, 0.85, 'Selected Components(23)', color='r')
11 [Link](loc="lower right")
12 [Link]()
In [164]:
1 #23 components are showing 90% of the variance in our data,so we'll build our PCA again wi
In [165]:
1 pca=PCA(n_components=23)
In [166]:
1 df3=[Link](pca.fit_transform(df2))
In [167]:
1 [Link]()
Out[167]:
0 1 2 3 4 5 6 7 8
5 rows × 23 columns
Clustering
[Link] Clustering
In [168]:
In [169]:
1 wcss=[]
In [170]:
1 for i in range(1,5):
2 kmean=KMeans(n_clusters=i,init="random",random_state=42)
3 [Link](df3)
4 [Link](kmean.inertia_)
In [171]:
1 print(wcss)
1 [Link](figsize=(10,5))
2 [Link](range(1,5),wcss,color="blue",linewidth=3)
3 [Link]('WCSS vs Number of Clusters')
4 [Link]('Number of Clusters')
5 [Link]('WCSS')
6 [Link]()
In [209]:
1 [Link](figsize=(10,5))
2 [Link](range(1,5),wcss,color="blue",linewidth=3)
3 [Link]('WCSS vs Number of Clusters')
4 [Link](2,wcss[1],s=200,c="cyan",marker="*",linewidth=5)
5 [Link]('Number of Clusters')
6 [Link]('WCSS')
7 [Link]()
In [174]:
In [175]:
1 silhouette_score_lst=[]
2
3 for i in range(2,6):
4 silhouette_score_lst.append(silhouette_score(df3,(KMeans(n_clusters=i).fit_predict(df3
In [176]:
1 silhouette_score_lst
Out[176]:
[0.18256678718476013,
0.12731079376982762,
0.12685890301831806,
0.1261638680247387]
In [212]:
1 #plotting
2 [Link](figsize=(10,5))
3
4 k=[2,3,4,5]
5 [Link](k,silhouette_score_lst,color="blue",edgecolor="red")
6
7 [Link]("Number of clusters")
8 [Link]("silhouette_score")
9 [Link]()
Elbow Graph shows that we should select 2 clusters and also silhouette scores is maximum when clusters
are 2
In [178]:
In [179]:
1 kmeans=KMeans(n_clusters=2,random_state=42)
In [180]:
1 [Link](df3)
Out[180]:
KMeans(n_clusters=2, random_state=42)
In [181]:
1 pred=[Link](df3)
2 pred
Out[181]:
In [213]:
1 x = [Link][:, 0]
2 y = [Link][:, 1]
3 [Link](figsize=(10,5))
4 [Link](x, y, c=pred,edgecolors="cyan")
5 [Link]('PC1')
6 [Link]('PC2')
7 [Link]('Cluster Visualization (PCA)')
8 [Link]()
In [220]:
1 x = [Link][:, 0]
2 y = [Link][:, 22]
3 [Link](figsize=(10,5))
4 [Link](x, y, c=pred,edgecolors="cyan")
5 [Link]()
In [184]:
1 # visualzing all
2 [Link](figsize=(10,5))
3
4 [Link].scatter_matrix([Link](df3), c=pred, figsize=(15,15), diagonal='hist')
5 [Link]()
1 #Viewing subset of it
2 dim_subset = [0, 1, 2, 4, 5,12,17,21]
3 [Link].scatter_matrix([Link]([Link][:, dim_subset]), c=pred, figsize=(15,15),
4 [Link]()
In [186]:
[Link] Clustering
In [187]:
In [223]:
1 [Link](figsize=(15,12))
2 den=[Link]([Link](df3,method="ward"))
3 [Link](y=100,color="r",linestyle="--")
4 [Link]()
Hierachial is showing with 100 clusters so its definately not a good approach.
[Link]
In [189]:
In [190]:
1 dbscan=DBSCAN(eps=27,min_samples=70)
2 [Link](df3)
Out[190]:
DBSCAN(eps=27, min_samples=70)
In [191]:
1 #Checking labels
2 dbscan.labels_
Out[191]:
In [192]:
1 #Total no of clusters
2 len(set(dbscan.labels_))
Out[192]:
In [193]:
Out[193]:
0.7784382051017664
In [258]:
Conclusions:
[Link] KMeans and DBSCAN were able to efficiently separate out the clusters in the dataset.
[Link], when comparing the two algorithms using the silhouette score, DBSCAN outperformed KMeans with a
score of 0.7784, while KMeans had a score of 0.182. This suggests that, despite the fact that KMeans appeared to
perform better visually, the clusters produced by DBSCAN were more distinct and better separated.
[Link], based on the silhouette score, DBSCAN is a better clustering algorithm for this dataset.
Project By:
Rohit Vyavahare
Linkedin [Link]
([Link]
Github : [Link]
([Link]