项目背景
以竞品分析为背景,通过数据的聚类,为汽车提供聚类分类。对于指定的车型,可以通过聚类分析找到其竞品车型。通过这道赛题,鼓励学习者利用车型数据,进行车型画像的分析,为产品的定位,竞品分析提供数据决策。
数据概况
读取数据并查看基础信息
import pandas as pd
car_df=pd.read_csv('car_price.csv')
car_df.head()
car_df.info()
数据量不大,只有205行。并且均无空值。
数据处理和清洗
筛选出数值型变量和分类变量。对于kmeans聚类算法,目前查阅到的资料是没有处理分类变量的办法的,所以我们过滤掉分类型变量。
import matplotlib.pyplot as plt import seaborn as sns numerical_fea = list(car_df.select_dtypes(exclude=['object']).columns) category_fea = list(filter(lambda x: x not in numerical_fea,list(car_df.columns)))
对连续型变量的分布做个可视化,对于偏度较高的数据,考虑取log处理。
fig, axs = plt.subplots(ncols=math.ceil(len(numerical_fea)/4), nrows=4, figsize=(12, 16))
plt.subplots_adjust(right=1.5, top=1.25)
for i, feature in enumerate(numerical_fea,1):
plt.subplot(math.ceil(len(numerical_fea)/4), 4, i)
g=sns.distplot(car_df[feature], color="m", label="Skewness : %.2f"%(car_df[feature].skew()))
g.legend(loc="best")
plt.xlabel('{}'.format(feature), size=20, labelpad=15)
plt.ylabel('density', size=20, labelpad=15)
plt.show()
对偏度大于1的enginesize,horsepower,price,wheelbase,compressionratio五个变量取log。
car_df1=car_df.copy()
car_df1['enginesize']=np.log(car_df1['enginesize'])
car_df1['horsepower']=np.log(car_df1['horsepower'])
car_df1['price']=np.log(car_df1['price'])
car_df1['wheelbase']=np.log(car_df1['wheelbase'])
car_df1['compressionratio']=np.log(car_df1['compressionratio'])
log_feature=['enginesize','horsepower','price','wheelbase','compressionratio']
fig, axs = plt.subplots(ncols=math.ceil(len(log_feature)/3), nrows=3, figsize=(12, 16))
plt.subplots_adjust(right=1.5, top=1.25)
for i, feature in enumerate(log_feature,1):
plt.subplot(math.ceil(len(log_feature)/3), 3, i)
g=sns.distplot(car_df1[feature], color="m", label="Skewness : %.2f"%(car_df1[feature].skew()))
g.legend(loc="best")
plt.xlabel('{}'.format(feature), size=20, labelpad=15)
plt.ylabel('density', size=20, labelpad=15)
plt.show()
除了compressionration之外,另外4个变量的偏度取log后都小于1。对于compressionratio,可以看到该变量的分布有两个峰,目前并未找到处理这类变量的合适方法,故不做进一步处理。
kmeans聚类为了消除变量间的量纲差异,需要对数据进行归一化处理。
std=preprocessing.StandardScaler() numerical_fea=[fea for fea in numerical_fea if fea not in ['car_ID']] std.fit(car_df1[numerical_fea]) car_df_std_lst1=std.transform(car_df1[numerical_fea]) car_df_std1=pd.DataFrame(car_df_std_lst1,columns=numerical_fea) print (car_df_std1)
数据建模
我们用肘部法则判断需要簇的数量。
sse = []
scope = range(1,15)
for k in scope:
kmeans = KMeans(n_clusters=k)
kmeans.fit(car_df_std1)
sse.append(kmeans.inertia_)
plt.xticks(scope)
plt.plot(scope,sse,marker="o")
但是上图的拐点并不明显,所以考虑用sc值进行判断。
xlable=[]
ylable=[]
std.fit(car_df1[numerical_fea])
car_df_std_lst1=std.transform(car_df1[numerical_fea])
car_df_std1=pd.DataFrame(car_df_std_lst1,columns=numerical_fea)
X = car_df_std1
for i in range(2,60):
kmeansmodel = KMeans(n_clusters=i,random_state = 0)
y_kmeans = kmeansmodel.fit_predict(X)
car_df1['car_type']=pd.Series(y_kmeans)
score=metrics.silhouette_score(X,y_kmeans,metric='euclidean')
xlable.append(i)
ylable.append(score)
# print (ylable)
plt.plot(np.array(xlable),np.array(ylable))
大约从10开始,sc值随着簇数的增加上升,但簇数太多并不合理,从图来看,当簇数大于40之后,sc值上升趋势放缓,所以簇数我们取40.
kmeansmodel = KMeans(n_clusters=40,random_state = 0) # 选择40作为聚类个数
y_kmeans = kmeansmodel.fit_predict(X)
car_df['car_type']=pd.Series(y_kmeans)
compete_map={}
# print (car_df.columns)
for carname in car_df['CarName']:
if 'volkswagen' in carname:
cartype=np.array(car_df[car_df['CarName']==carname]['car_type'])[0]
if carname not in list(compete_map.keys()):
compete_map[carname]=[i for i in car_df['CarName'][car_df['car_type']==cartype].tolist() if not 'volkswagen' in i]
else:
compete_map[carname].extend([i for i in car_df['CarName'][car_df['car_type']==cartype].tolist() if not 'volkswagen' in i])
# print (compete_map)
for k,v in compete_map.items():
print (k,'的竞品是:',v)
Volkswagen各型号的竞品如下:



