1 import pandas as pd
  2 import numpy as np
  3 from sklearn.cluster import KMeans
  4 import matplotlib.pyplot as plt
  5 
  6 
  7 def stand_sca(data):
  8     """
  9     标准差标准化
 10     :param data:原数据
 11     :return: 标准差之后的数据
 12     """
 13     data = (data - data.mean()) / data.std()
 14 
 15     return data
 16 
 17 
 18 def box_analysis(data):
 19     """
 20     箱线图分析去除异常值
 21     :param data: 原数据---series
 22     :return: bool数组
 23     """
 24     # 上四分位数
 25     qu = data.quantile(q=0.75)
 26     # 下四分位数
 27     ql = data.quantile(q=0.25)
 28     # 计算四分位间距
 29     iqr = qu - ql
 30 
 31     # 上限
 32     up = qu + 1.5 * iqr
 33     # 下限
 34     low = ql - 1.5 * iqr
 35 
 36     bool_index = (data < up) & (data > low)
 37 
 38     return bool_index
 39 
 40 
 41 # 1、了解航空公司现状以及 航空用户的价值
 42 # 6w+ 样本  44个特征 ----> LRFMC
 43 # 2、数据处理
 44 # (1)缺失值处理
 45 # 删除法
 46 #  (2) 筛选相关特征---构建最终的特征
 47 # LRFMC <----筛选出能够构建这5个特征的相关特征
 48 # (3)异常值处理
 49 # 3sigma 原则 或者 box_analysis
 50 # (4)标准化处理
 51 # 标准差标准化
 52 # 3、k-means实现航空用户的聚类
 53 # sklearn
 54 # 4、结果展示
 55 # 绘制雷达图
 56 # 5、输出结论
 57 # 营销策略
 58 
 59 def build_data():
 60     """
 61     构建原始数据
 62     :return: 原始数据
 63     """
 64     # 1、加载数据
 65     air_data = pd.read_csv("./air_data.csv", encoding="ansi")
 66     # print("air_data:\n", air_data)
 67     # print("air_data 的列索引名称:\n", air_data.columns)
 68 
 69     return air_data
 70 
 71 
 72 def deal_data(air_data):
 73     """
 74     数据处理
 75     :param air_data:原始数据
 76     :return: 数据处理之后的结果
 77     """
 78     # 2、数据清洗
 79     # 缺失值、异常值
 80     # 检测缺失值
 81     res_null = pd.isnull(air_data).sum()
 82     # print("缺失值检测结果:", res_null)
 83 
 84     # 处理缺失值
 85     # (1)丢弃票价为空的记录 # SUM_YR_1  SUM_YR_2两列
 86     # ----可以理解保留票价不为空
 87     bool_index_1 = air_data.loc[:, "SUM_YR_1"].notnull()
 88     bool_index_2 = air_data.loc[:, "SUM_YR_2"].notnull()
 89     # 个人认为 只有两列票价都不为空,票价才不为空
 90     bool_index = bool_index_1 & bool_index_2
 91     air_data = air_data.loc[bool_index, :]
 92     # (2)丢弃票价为0,折扣不为0,飞行里程 > 0 的数据--->丢弃航空公司没有盈利的数据
 93     # 保留盈利的数据
 94     # 保留票价 > 0,折扣 > 0,飞行里程 > 0
 95     # 个人认为只要有一列票价>0,票价就>0
 96     bool_id_1 = air_data.loc[:, "SUM_YR_1"] > 0
 97     bool_id_2 = air_data.loc[:, "SUM_YR_2"] > 0
 98 
 99     # 折扣> 0
100     bool_id_3 = air_data.loc[:, "avg_discount"] > 0
101 
102     # 飞行里程>0
103     bool_id_4 = air_data.loc[:, "SEG_KM_SUM"] > 0
104 
105     bool_id = (bool_id_1 | bool_id_2) & bool_id_3 & bool_id_4
106 
107     air_data = air_data.loc[bool_id, :]
108 
109     res_null = pd.isnull(air_data).sum()
110     # print("缺失值检测结果:", res_null)
111 
112     # 先筛选特征
113     # LRFMC
114     # 筛选 入会时间、窗口结束时间、最后乘坐飞机距离窗口结束的时长,乘坐飞机次数、飞行里程、折扣系数
115     air_data = air_data.loc[:, ["FFP_DATE", "LOAD_TIME", "LAST_TO_END", "FLIGHT_COUNT", "SEG_KM_SUM", "avg_discount"]]
116 
117     # 构建LRFMC五个特征
118     air_data.loc[:, "FFP_DATE"] = pd.to_datetime(air_data.loc[:, "FFP_DATE"])
119     air_data.loc[:, "LOAD_TIME"] = pd.to_datetime(air_data.loc[:, "LOAD_TIME"])
120     # 获取时间差--单位是day
121     air_data.loc[:, "L_days"] = air_data.loc[:, "LOAD_TIME"] - air_data.loc[:, "FFP_DATE"]
122     # 获取相差天数 的数值
123     air_data.loc[:, "L_days"] = [i.days for i in air_data.loc[:, "L_days"]]
124     # 获取具体的月数--即L
125     air_data.loc[:, "L"] = np.ceil(air_data.loc[:, "L_days"] / 30)
126     # print(air_data.loc[:, "L"])
127     # 构建R --- LAST_TO_END 这个时长应该是天数
128     # print(air_data.loc[:, "LAST_TO_END"])
129     air_data.loc[:, "R"] = np.ceil(air_data.loc[:, "LAST_TO_END"] / 30)
130     # print("air_data.loc[:, "R"]:\n",air_data.loc[:, "R"])
131 
132     air_data.loc[:, "F"] = air_data.loc[:, "FLIGHT_COUNT"]
133 
134     air_data.loc[:, "M"] = air_data.loc[:, "SEG_KM_SUM"]
135 
136     air_data.loc[:, "C"] = air_data.loc[:, "avg_discount"]
137 
138     air_data = air_data.iloc[:, -5:]
139     # print("最终的数据:\n", air_data)
140 
141     # 异常值处理
142     for column in air_data.columns:
143         bool_ = box_analysis(air_data.loc[:, column])
144         air_data = air_data.loc[bool_, :]
145 
146     # 标准化数据
147     air_data = stand_sca(air_data)
148 
149     print("标准化之后的数据:\n", air_data)
150 
151     return air_data
152 
153 
154 def km_fit(air_data, k):
155     """
156     k-means训练数据,并进行用户聚类
157     :param air_data: 数据
158     :param k: 聚类的类别数目
159     :return:
160     """
161     # 1、创建算法实例
162     km = KMeans(n_clusters=k)
163     # 2、训练数据
164     km.fit(air_data)
165     # 3、预测
166     y_predict = km.predict(air_data)
167 
168     # 获取聚类中心
169     center = km.cluster_centers_
170 
171     return y_predict, center
172 
173 
174 def show_res(center, feature_num):
175     """
176     绘制雷达图
177     :param center:聚类中心
178     :param feature_num: 特征的数量
179     :return:
180     """
181     # 1、创建画布
182     # 绘制雷达图 需要用到极坐标
183     fig = plt.figure()
184     # 修改RC参数,来让其支持中文
185     plt.rcParams['font.sans-serif'] = 'SimHei'
186     plt.rcParams['axes.unicode_minus'] = False
187     # polar  开启极坐标
188     fig.add_subplot(1, 1, 1, polar=True)
189     # 2、绘图
190     # 准备数据
191     # 准备角度数据
192     angle = np.linspace(start=0, stop=2 * np.pi, num=feature_num, endpoint=False)
193     print(angle)
194     # 闭合角度
195     angle = np.concatenate((angle, [angle[0]]))
196     # print(angle)
197     for i in range(center.shape[0]):
198         # print(center[i, 0])
199         # 闭合数据
200         data = np.concatenate((center[i, :],[center[i, 0]]))
201         plt.polar(angle, data)
202 
203     # 设置刻度
204     plt.xticks(angle[:-1],["L","R","F","M","C"])
205 
206     # 增加图例
207     plt.legend(["第一类用户","第二类用户","第三类用户","第四类用户","第五类用户"])
208     # 保存图片
209     plt.savefig("./航空用户聚类分析结果雷达图展示.png")
210     # 3、展示
211     plt.show()
212 
213 
214 def main():
215     """
216     主函数
217     :return:
218     """
219     # 1、构建原始数据
220     air_data = build_data()
221     # 2、数据处理
222     air_data = deal_data(air_data)
223     # 3、构建聚类模型进行用户聚类
224     # 确定聚类的类别数目
225     k = 5
226     y_predict, center = km_fit(air_data, k)
227     print("预测值为:\n", y_predict)
228     print("聚类中心为:\n", center)
229 
230     # 4、结果展示
231     show_res(center, center.shape[1])
232 
233 
234 if __name__ == '__main__':
235     main()