2023 年第二届钉钉杯大学生大数据挑战赛初赛题目 初赛 A:智能手机用户监测数据分析
一、问题背景
近年来,随着智能手机的产生,发展到爆炸式的普及增长,不仅推动了中 国智能手机市场的发展和扩大,还快速的促进手机软件的开发。近年中国智能手 机市场品牌竞争进一步加剧,中国超越美国成为全球第一大智能手机市场。手机 软件日新月异,让人们更舒适的使用手机,为人们的生活带来很多乐趣,也产生 了新的群体“低头一族”。手机软件进入人们的生活,游戏、购物、社交、资讯、理财等等APP吸引着、方便着现代社会的人们,让手机成为人们出门的必备物 品。该数据来自某公司某年连续30天的4万多智能手机用户的监测数据,已经做 了脱敏和数据变换处理。每天的数据为1个txt文件,共10列,记录了每个用户(以uid为唯一标识)每天使用各款APP(以appid为唯一标识)的起始时间,使 用时长,上下流量等。具体说明见表1。此外,有一个辅助表格, app_class.csv,共两列。第一列是appid,给出4000多个常用APP所属类别(app_class),比如:社交类、影视类、教育类等,用英文字母a-t表示,共20个常 用得所属类别,其余APP不常用,所属类别未知。
表 1
变量编号 | 变量名 | 释义 |
---|---|---|
1 | uid | 用户的id |
2 | appid | APP的id(与app_class文件中的第一列对应) |
3 | app_type | APP类型:系统自带、用户安装 |
4 | start_day | 使用起始天,取值1-30(注:第一天数据的头两行的使用起始天取 值为0,说明是在这一天的前一天开始使用的) |
5 | start_time | 使用起始时间 |
6 | end_day | 使用结束天 |
7 | end_time | 使用结束时间 |
8 | duration | 使用时长(秒) |
9 | up_flow | 上行流量 |
10 | down_flow | 下行流量 |
二、解决问题
(一)根据用户常用所属的20类APP的数据对用户进行聚类,要求至少给出三种不同的聚 类算法进行比较,选择合理的聚类数量K值,并分析聚类结果。
(二)根据聚类结果对不同类别的用户画像,并且分析不同群体用户的特征。(用户画 像定义:根据用户的属性,偏好,行为习惯等信息对用户打标签,用以描述不同群体的用户 行为,从而针对不同群体的用户推荐不同所属类别的APP产品。)
(一)对用户使用APP的情况进行预测,根据用户第1~11天的a类APP的使用情况,来预 测用户在第12~21天是否会使用该类APP。给出预测结果和真实结果相比的准确率。(注:测 试集不能参与到训练和验证中,否则作违规处理)
(二)对用户使用APP的情况进行预测,根据用户第1~11天的a类APP的使用情况,来预测 第12~21天用户使用a类APP的有效日均使用时长。评价指标选用NMSE。
M M S E = ∑ ( y i − y i ^ ) ∑ ( y i − y i ‾ ) MMSE = \sqrt{\frac{\sum(y_i-\hat{y_i})}{\sum(y_i-\overline{y_i})}} MMSE=∑(yi−yi)∑(yi−yi^)
式中, y i y_i yi表示使用时长的实际值; y i ^ \hat{y_i} yi^表示使用时长的预测值; y i ‾ \overline{y_i} yi表示所有用户的实际使用时长的平均值。给出预测结果和真实结果之间的NMSE。(注:测试集不能参与到训练和验证中, 否则作违规处理)
第一题:
数据预处理:对用户常用的20类APP数据,进行数据清洗和特征提取。可以使用PCA、LDA算法进行降维,减小计算复杂度。
聚类算法:
a. K-means: 进行数据聚类时,选择不同的K值进行多次试验,选取最优的聚类结果。可以使用轮廓系数、Calinski-Harabaz指数等评价指标进行比较和选择。
b. DBSCAN: 利用密度对数据点进行聚类,不需要预先指定聚类的数量。使用基于密度的聚类算法时,可以通过调整半径参数和密度参数来得到不同聚类效果。
c. 层次聚类:可分为自顶向下和自底向上两种方式。通过迭代计算每个数据点之间的相似度,将数据点逐渐合并,最后得到聚类结果。
d.改进的聚类算法
e. 深度聚类算法
聚类结果分析:选择最优的聚类结果后,对不同类别用户进行画像。分析每个类别的用户行为特征(如使用时段、使用频率、使用时长、使用偏好等),根据用户画像为用户打标签。根据用户标签,推荐不同所属类别的APP产品。
第二题:
导入包
import pandas as pd
from sklearn.cluster import Birch
from sklearn.cluster import AgglomerativeClustering
from sklearn.decomposition import PCA
import time
from sklearn import metrics
import os
from sklearn.cluster import MeanShift
from tqdm import tqdm
import numpy as np
import warnings
warnings.filterwarnings("ignore")
tqdm.pandas()
合并数据
# 合并数据
folder_path = '初赛数据集/'
dfs = []
for filename in os.listdir(folder_path):
if filename.endswith('.txt'):
csv_path = os.path.join(folder_path, filename)
tempdf = pd.read_csv(csv_path)
dfs.append(tempdf)
df = pd.concat(dfs,axis=0)
df.shape
数据清洗:
import pandas as pd
import datetime
import matplotlib.pyplot as plt
# 数据清洗
df.loc[df['start_day'] == 0, 'start_day'] = 1 # 将使用起始天为0的行,修改为1
df['start_time'] = pd.to_datetime(df['start_time']) # 转换为datetime类型
df['end_time'] = pd.to_datetime(df['end_time']) # 转换为datetime类型
df['usage_time'] = (df['end_time'] - df['start_time']) / pd.Timedelta(minutes=1) # 使用时长(分钟)
df['up_flow_mb'] = df['up_flow'] / 1024 / 1024 # 上行流量(MB)
df['down_flow_mb'] = df['down_flow'] / 1024 / 1024 # 下行流量(MB)
df = df[df['duration'] != 0] # 剔除使用时长为0的行
df = df[df['up_flow'] != 0] # 剔除上行流量为0的行
df = df[df['down_flow'] != 0] # 剔除下行流量为0的行
# 剔除使用时长和流量明显异常的行
# 剔除使用时长小于10秒的行
df = df[df['usage_time'] >= 10]
fig, axs = plt.subplots(1, 3, figsize=(10, 5))
axs[0].hist(df['usage_time'])
axs[0].set_title('Usage Time')
axs[0].set_xlabel('Time (minutes)')
axs[1].hist(df['up_flow_mb'])
axs[1].set_title('Up Flow')
axs[1].set_xlabel('Up Flow (MB)')
axs[2].hist(df['down_flow_mb'])
axs[2].set_title('Down Flow')
axs[2].set_xlabel('Down Flow (MB)')
plt.show()
df
# APP分类信息(可根据app_id和app_class文件进行关联)
cate_df = pd.read_csv('初赛数据集/app_class.csv',header=None)
cate_df.columns = ['appid','letter']
# 定义字母编码映射字典
char_map = {chr(i + 96): i for i in range(1, 27)}
# 将'letter'列中的字母进行编码
cate_df['letter'] = cate_df['letter'].map(char_map)
cate_dict = dict(zip(cate_df['appid'],cate_df['letter']))
df['category'] = df['appid'].map(cate_dict)
# 用户的使用次数、使用总时长、总流量、平均每次使用时长、平均流量等特征
user_agg = df.groupby('uid').agg({'appid': 'nunique', 'usage_time': ['sum', 'mean'],
'up_flow_mb': ['sum', 'mean'], 'down_flow_mb': ['sum', 'mean']})
user_agg.columns = ['num_apps', 'total_usage_time', 'avg_usage_time',
'total_up_flow', 'avg_up_flow', 'total_down_flow', 'avg_down_flow']
# APP的使用次数、使用总时长、总流量、平均每次使用时长、平均流量等特征
app_agg = df.groupby('appid').agg({'uid': 'nunique', 'usage_time': ['sum', 'mean'],
'up_flow_mb': ['sum', 'mean'], 'down_flow_mb': ['sum', 'mean']})
app_agg.columns = ['num_users', 'total_usage_time', 'avg_usage_time',
'total_up_flow', 'avg_up_flow', 'total_down_flow', 'avg_down_flow']
app_agg['category'] = app_agg.index.map(cate_dict)
app_agg
。。。略
app_agg['category'] = app_agg.index.map(cate_dict)
app_agg
。。。略
user_daily_agg.columns = ['avg_num_apps', 'avg_daily_usage_time', 'avg_daily_up_flow', 'avg_daily_down_flow']
user_daily_agg['total_days'] = user_dates
user_daily_agg
。。。略
user_app_dates_agg.columns = ['min_app_dates', 'avg_app_dates', 'max_app_dates']
user_app_dates_agg
。。。略
app_daily_agg['total_days'] = app_dates.groupby('appid').size()
app_daily_agg
# 合并特征
merged_df_uid = pd.concat([user_agg,user_daily_agg,user_app_dates_agg], axis=1, join='inner')
merged_df_appid = pd.concat([app_agg,app_daily_agg], axis=1, join='inner')
raw_df = df[['uid','appid']]
all_df = pd.merge(raw_df,merged_df_uid,on='uid')
all_df = pd.merge(all_df,merged_df_appid,on='appid')
all_df = all_df.drop_duplicates(subset='uid')
all_df = all_df.dropna()
# 输出结果
all_df.to_excel('初赛数据集/all_df.xlsx', index=False)
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
# 对df进行归一化
df = pd.read_excel('初赛数据集/all_df.xlsx')
df = df.drop(columns=['uid','appid'])
scaler = MinMaxScaler()
weight = scaler.fit_transform(df)
start = time.time()
trainingData = weight
SSE = [] # 存放每次结果的误差平方和
k1 = 2
k2 = 10
for k in range(k1, k2):
pca = PCA(n_components=k)
trainingData = pca.fit_transform(weight)
estimator = KMeans(n_clusters=k, max_iter=10000, init="k-means++", tol=1e-6)
estimator.fit(trainingData)
SSE.append(estimator.inertia_) # estimator.inertia_获取聚类准则的总和
end = time.time()
print(f'耗时:{end-start}s')
X = range(k1,k2)
plt.figure(figsize=(8,6))
plt.xlabel('k',fontsize=20)
plt.ylabel('SSE',fontsize=20)
plt.plot(X, SSE, 'o-')
plt.savefig('img/pca降维-手肘法.png',dpi=300)
plt.show()
from sklearn.cluster import KMeans
start = time.time()
pca = PCA(n_components=10)
trainingData = pca.fit_transform(weight)
# trainingData = weight
clf = KMeans(n_clusters=4,max_iter=10000, init="k-means++", tol=1e-6)
result = clf.fit(trainingData)
source = list(clf.predict(trainingData))
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
CHI = metrics.calinski_harabasz_score(trainingData, label)
print("CHI: ", CHI)
start = time.time()
pca = PCA(n_components=10)
trainingData = pca.fit_transform(weight)
# 使用层次聚类
clf = AgglomerativeClustering(n_clusters=4, linkage='ward', affinity='euclidean')
result = clf.fit(trainingData)
source = list(clf.labels_)
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
CHI = metrics.calinski_harabasz_score(trainingData, label)
print("CHI: ", CHI)
start = time.time()
pca = PCA(n_components=10)
trainingData = pca.fit_transform(weight)
# 进行PCA降维
pca = PCA(n_components=10)
trainingData = pca.fit_transform(weight)
# 使用均值漂移聚类
clf = MeanShift(bandwidth=0.9)
result = clf.fit(trainingData)
source = list(clf.labels_)
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
CHI = metrics.calinski_harabasz_score(trainingData, label)
print("CHI: ", CHI)
from sklearn.cluster import DBSCAN
from sklearn.decomposition import PCA
import time
from sklearn import metrics
start = time.time()
pca = PCA(n_components=10)
trainingData = pca.fit_transform(weight)
trainingData = weight
clf = DBSCAN(eps=0.08, min_samples=7)
result = clf.fit(trainingData)
source = list(clf.fit_predict(trainingData))
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
CHI = metrics.calinski_harabasz_score(trainingData, label)
print("CHI: ", CHI)
pca = PCA(n_components=10)
trainingData = pca.fit_transform(weight)
trainingData = weight
clf = Birch(n_clusters=5, branching_factor=10, threshold=0.01)
start = time.time()
result = clf.fit(trainingData)
source = list(clf.predict(trainingData))
end = time.time()
label = clf.labels_
print(f'耗时:{end-start}s')
silhouette = metrics.silhouette_score(trainingData, label)
print("silhouette: ", silhouette)
CHI = metrics.calinski_harabasz_score(trainingData, label)
print("CHI: ", CHI)
见知乎文章底部
zhuanlan.zhihu.com/p/643785015