python数据分析:用户消费情况数据分析

2023-11-07

本次分析数据介绍:

数据为某奶茶店2018年1月-2019年6月的销售数据,共计69,659项数据,用户共计23,570名;

数据集共4个字段:

user_id: 用户id
order_id: 购买日期
order_prodect: 购买产品数
order_account: 购买金额
明确问题(本次数据分析目的)
用户消费趋势分析(按月)
用户个体消费分析
用户消费行为分析
用户复购率和回购率分析
分析思路
在这里插入图片描述
理解数据
我们导入数据后简单看一下:

# 导入数据
df = pd.read_excel('MilkyTea_master.xlsx')

在这里插入图片描述
检查一下各字段类型:在这里插入图片描述
数据清洗
本次抽样为全抽,不再进行选择子集;不再涉及列重命名;

1.数据类型转换

根据以上分析,我们需把用户id转化为字符串,把时间order_id转化为时间格式:

① 时间类型转换

# 时间类型转换,增加‘month’列
df['order_dt'] = pd.to_datetime(df.order_dt,format='%Y%m%d')
df['month'] = df.order_dt.values.astype('datetime64[M]')

在这里插入图片描述
② 字符串类型转换

df['user_id'] = df['user_id'].apply(lambda x : str(x))

2.数据排序

# 按照“order_dt”排序
df = df.sort_values(by = 'order_dt',ascending =True)

3.缺失值处理

我们检查是否存在缺失值:

for i in df.columns:
     print(df[i].isnull().value_counts())

在这里插入图片描述
无需进行缺失值处理;
4.异常值处理

检查描述统计信息,查看是否存在异常
在这里插入图片描述
以上,我们可以发现:

大部分订单只购买了少量商品(平均值2.41),有一定极值干扰;
影虎的消费金额比较稳定(平均值35.89元,中位数25.98元),有一定极值干扰;
最后,我们对index重新命名:

df = df.reset_index(drop = True)

在这里插入图片描述
构建模型
接下来,我们对问题进行分析:

1.用户消费趋势分析(按月)

我们按月度对用户进行分析,须将数据框按月分组:

g_month  = df.groupby('month')

1 每月消费金额

# 每月消费总金额
g_month.order_account.sum()

2 每月消费次数

# 每月消费总次数
g_month.user_id.count()

3 每月购买数量

# 每月购买数量
g_month.order_products.sum()

4 每月消费人数

# 每月消费人数
g_month.user_id.apply(lambda x : len(x.drop_duplicates()))

以上我们也可用以下方式来统一分析:

df_table = df.pivot_table(index = 'month',
              values = ['order_products','order_account','user_id'],
              aggfunc = {'order_products':'sum',
                       'order_account':'sum',
                       'user_id':'count'})

在这里插入图片描述
我们将分析结果进行数据可视化

sns.set_palette('summer')
sns.set_style('darkgrid')
f = plt.figure(figsize = (20,16))
f.add_subplot(3,1,1)
g_month.order_account.sum().plot()
plt.title('order_account')
f.add_subplot(3,1,2)
g_month.order_products.sum().plot()
plt.title('order_products')
f.add_subplot(3,1,3)
g_month.user_id.count().plot()
plt.title('user_id')

在这里插入图片描述

2.用户个体消费

与按月分析不同,此次需要对数据库按用户id进行分组:

1.用户消费金额、消费次数的描述统计

# 按照用户进行分组
g_user = df.groupby('user_id')
# 用户消费金额、消费次数的描述统计
g_user.sum().describe()

在这里插入图片描述
用户平均购买奶茶7杯,中位数为3杯,说明小部分的用户购买了大量的奶茶;
用户平均消费106元,中位数43元,说明小部分用户购买大量的奶茶,存在极值干扰;
2.用户消费金额和消费次数的散点图

# 用户消费金额和消费次数的散点图
g_user.sum().plot.scatter(x = 'order_account', y = 'order_products')

在这里插入图片描述
不是那么明显,我们选择购买金额<6000的数据集进行分析(去除极值):

# 用户消费金额和消费次数的散点图
g_user.sum().query('order_account < 6000').plot.scatter(x = 'order_account', y = 'order_products')

在这里插入图片描述
3.用户消费金额分布图

# 用户消费金额分布图
g_user.sum().order_account.plot.hist(bins = 20)

在这里插入图片描述
可知,用户消费金额大部分呈集中趋势,小部分异常值干扰了判断;

我们可以运用切比雪夫定理:‘所有数据中,至少有24/25(或96%)的数据位于平均数5个标准差范围内’,故,我们用7+16.98*5 ≈100 来排出异常值:

g_user.sum().query('order_account < 100').order_account.plot.hist(bins = 20)

在这里插入图片描述
4**用户累计消费占比(**百分之多少的用户占百分之多少的销售额)

# 用户累计消费占比(百分之多少的用户占百分之多少的销售额)
g_user.sum().sort_values('order_account').apply(lambda x : x.cumsum()/ x.sum()).reset_index().order_account.plot()

在这里插入图片描述

3.用户消费行为分析

同样,我们依旧按用户分组进行分析:
1)用户第一次消费(首购)

# 用户第一次消费(首购)
g_user.min().order_dt.value_counts().plot()

在这里插入图片描述
用户首购分布:集中在前3月;在2月中旬期间,波动较剧烈;

2.用户最后一次消费

# 用户最后一次消费
g_user.max().order_dt.value_counts().plot()

在这里插入图片描述
用户最后一次购买时间分布较广,大部分最后一次购买集中在前3个月,说明可能存在很多用户购买了一次没有再复购;随着时间递增,最后一次购买的用户数量也在上升,消费呈流失上升状态;
3.新老客户消费比

① 多少用户仅消费1次

# 多少用户仅消费1次
user_life = g_user.order_dt.agg(['min','max'])
(user_life['min'] == user_life['max']).value_counts()
# 可视化(饼图)
plt.pie((user_life['min'] == user_life['max']).value_counts(normalize = True),
       autopct='%.2f%%',
       labels = (user_life['min'] == user_life['max']).value_counts().index)

在这里插入图片描述
我们发现,超过一半用户仅消费一次;

② 每月新客占比

# 每月新客占比
g_user.min().month.value_counts() / g_month.user_id.apply(lambda x : len(x.drop_duplicates()))

在这里插入图片描述
我们发现,首月消费新客占比100%,第二、三月逐渐新客数量占比减少,第四月后无新客;

4.用户分层

① RFM用户分层

这里需要补充一下RFM分析:

RFM是3个指标的缩写,最近一次消费时间间隔(Recency),消费频率(Frequency),消费金额(Monetary)。通过这3个指标对用户分类。
最近一次消费时间间隔(R),上一次消费离得越近,也就是R的值越小,用户价值越高;
消费频率(F),购买频率越高,也就是F的值越大,用户价值越高;
消费金额(M),消费金额越高,也就是M的值越大,用户价值越高;

# RFM用户分层
rfm = df.pivot_table(index = 'user_id',
                    values = ['order_products','order_account','order_dt'],
                    aggfunc= {'order_products':'sum',
                             'order_account':'sum',
                             'order_dt':'max'})

在这里插入图片描述
我们对最后一次消费时间计时间间隔,并对透视结果列重命名:

rfm['R'] = (rfm.order_dt.max() - rfm.order_dt) / np.timedelta64(1,'D')
rfm.rename(columns={'order_products':'F','order_account':'M'},inplace = True)

在这里插入图片描述
建立RFM模型:

def rfm_func(x):
    level = x.apply(lambda x:'1' if x > 0 else '0')
    label = level.R +level.F + level.M
    d = {
        '111':'重要价值客户',
        '011':'重要保持客户',
        '101':'重要发展客户',
        '001':'重要挽留客户',
        '110':'一般价值客户',
        '010':'一般保持客户',
        '100':'一般发展客户',
        '000':'一般挽留客户'
    }
    result = d[label]
    return result
rfm['label'] = rfm[['R','F','M']].apply(lambda x : x - x.mean()).apply(rfm_func,axis=1)

在这里插入图片描述

接着,我们对‘label’列分组求和:

rfm.groupby('label').sum()

在这里插入图片描述
也可以分组后计数:

rfm.groupby('label').count()

在这里插入图片描述
从RFM分层可知,大部分的用户是一般挽留客户,重要价值客户和重要保持客户合计约占20%;

我们进行可视化:


rfm.loc[rfm.label == '重要价值客户','color'] = 'g'
rfm.loc[rfm.label != '重要价值客户','color'] = 'r'
rfm.plot.scatter('F','R',c = rfm.color)

在这里插入图片描述

② 用户生命周期-新、老、活跃、回流、流失

# 用户生命周期-新、老、活跃、回流、流失
pivoted_counts = df.pivot_table(index = 'user_id',
                               columns= 'month',
                               values= 'order_dt',
                               aggfunc= 'count').fillna(0)

在这里插入图片描述
需要对结果进行简化,如果购买次数大于0(大于等于1),有购买为1,无购买为0:

# 进行简化
df_purchase = pivoted_counts.applymap(lambda x : 1 if x>0 else 0)

在这里插入图片描述
根据本月是否消费、前期是否消费、上月是否消费等多层维度,将用户划分为未注册、新注册、活跃、不活跃、回流、流失;

def active_statu(data):
    status = []
    for i in range(18):
        # 当月未消费
        if data[i] == 0:
            if len(status) > 0:
                if status[i-1] == 'unreg':
                    status.append('unreg')
                else:
                    status.append('unactive')
            else:
                status.append('unreg')
        # 当月有消费
        else:
            if len(status) == 0:
                status.append('new')
            else:
                if status[i-1] == 'unactive':
                    status.append('return')
                elif status[i-1] == 'unreg':
                    status.append('new')
                else:
                    status.append('active')
                    
    return status

purshase_stats = df_purchase.apply(active_statu,axis=1)
purshase_stats = pd.DataFrame(purshase_stats)[0].apply(pd.Series)
purshase_stats.columns = pivoted_counts.columns

在这里插入图片描述
把‘unreg’用NAN替换,并对每月的用户状态进行统计:

purshase_stats_ct = purshase_stats.replace('unreg',np.NAN).apply(lambda x : pd.value_counts(x))
#  转置
purshase_stats_ct.fillna(0).T

在这里插入图片描述
可视化:

purshase_stats_ct.fillna(0).T.plot.area()

在这里插入图片描述
可以逐行计算百分比:

# 可以逐行计算百分比
purshase_stats_ct.fillna(0).T.apply(lambda x : x/x.sum(),axis = 1)
purshase_stats_ct.fillna(0).T.apply(lambda x : x/x.sum(),axis = 1).plot.area()

在这里插入图片描述
在这里插入图片描述
5)用户购买周期(按订单)

① 用户订单周期

# 用户订单周期
order_diff = g_user.apply(lambda x: x.order_dt - x.order_dt.shift())

在这里插入图片描述
查看描述统计:
在这里插入图片描述
② 用户订单分布

(order_diff/np.timedelta64(1,'D')).hist(bins=20)

在这里插入图片描述
订单周期呈指数分布
用户平均购买周期是68天
绝大部分用户购买周期都超过100天

6.用户生命周期(按第一次&最后一次消费)

我们查看用生命周期(第一次&最后一次)描述统计:

(user_life['max'] - user_life['min']).describe()

在这里插入图片描述

在这里插入图片描述
用户生命周期受只购买一次的用户影响较大
用户平均消费时间差134天,中位数仅0天

4.用户复购率和留存率
1)复购率

自然月内,购买多次的用户占比:

我们根据上述的对用户按月统计购买量pivoted_counts来分析:

如果当月购买了1次以上(大于1),则为1,存在复购;若购买了1次,为0;当月购买了0次,则为NAN;

purchase_r = pivoted_counts.applymap(lambda x : 1 if x > 1 else np.NAN if x == 0 else 0 )

求和,为当月复购的人数;计数,为当月购买人数:

(purchase_r.sum() / purchase_r.count()).plot(figsize = (10,4))

在这里插入图片描述
复购率稳定在20%左右,前三月因为有大量的新用户涌入,而这批用户只购买了一次,所以导致复购率低;
2.回购率(留存率)
曾经购买过的用户某一时间内再次购买的占比:

定义函数,如果当月购买,下月也购买为1;如果当月购买下月未购买,为0;如果当月未购买,则为NAN;

def purchase_back(data):
    status = []
    for i in range(17):
        if data[i] ==1:
            if data[i+1] == 1 :
                status.append(1)
            if data[i+1] == 0 :
                status.append(0)
        else:
            status.append(np.NAN)
    status.append(np.NAN)
    return status

purchase_b = df_purchase.apply(purchase_back,axis=1)
purchase_b = pd.DataFrame(purchase_b)[0].apply(pd.Series)
purchase_b.columns = pivoted_counts.columns

在这里插入图片描述
回购率 = 次月再次购买/本月购买人数

(purchase_b.sum()/purchase_b.count()).plot(figsize = (10,4))

在这里插入图片描述
分析结论
用户增长阶段仅在前三月,后期消费均为老客户;消费用户群体较为固定;
超过50%的用户仅在前三月消费了一次,后期长期处于不活跃状态;用户消费呈流失上升状态;
60%的销售金额是被前5000名(近20%)的人贡献。
用户增长阶段仅在前三月,后期消费均为老客户;消费用户群体较为固定;
超过50%的用户仅在前三月消费了一次,后期长期处于不活跃状态;用户消费呈流失上升状态;
60%的销售金额是被前5000名(近20%)的人贡献。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python数据分析:用户消费情况数据分析 的相关文章

  • 如何计算 pandas datetime 对象的均值和方差?

    如何计算 YYYY MM DD 形式的 python 日期时间对象的汇总统计数据 均值和标准差 我想对具有不同 ID 的不同日期时间对象组执行此操作 数据如下 import datetime as dt df pd DataFrame Da
  • 多输出堆叠回归器

    一次性问题 我正在尝试构建一个多输入堆叠回归器 添加到 sklearn 0 22 据我了解 我必须结合StackingRegressor and MultiOutputRegressor 经过多次尝试 这似乎是正确的顺序 import nu
  • 嵌套列表的重叠会产生不必要的间隙

    我有一个包含三个列表的嵌套 这些列表由 for 循环填充 并且填充由 if 条件控制 第一次迭代后 它可能类似于以下示例 a 1 2 0 0 0 0 0 0 4 5 0 0 0 0 0 0 6 7 根据条件 它们不重叠 在第二次迭代之后 新
  • 如何从Python中的函数返回多个值? [复制]

    这个问题在这里已经有答案了 如何从Python中的函数返回多个变量 您可以用逗号分隔要返回的值 def get name you code return first name last name 逗号表示它是一个元组 因此您可以用括号将值括
  • 使用主题交换运行多个 Celery 任务

    我正在用 Celery 替换一些自制代码 但很难复制当前的行为 我期望的行为如下 创建新用户时 应向tasks与交换user created路由键 该消息应该触发两个 Celery 任务 即send user activate email
  • 在 Django Admin 中调整字段大小

    在管理上添加或编辑条目时 Django 倾向于填充水平空间 但在某些情况下 当编辑 8 个字符宽的日期字段或 6 或 8 个字符的 CharField 时 这确实是一种空间浪费 字符宽 然后编辑框最多可容纳 15 或 20 个字符 我如何告
  • Tensorboard SyntaxError:语法无效

    当我尝试制作张量板时 出现语法错误 尽管开源代码我还是无法理解 我尝试搜索张量板的代码 但不清楚 即使我不擅长Python 我这样写路径C Users jh902 Documents logs因为我正在使用 Windows 10 但我不确定
  • 为什么一旦我离开内置的运行服务器,Django 就无法找到我的管理媒体文件?

    当我使用内置的简单服务器时 一切正常 管理界面很漂亮 python manage py runserver 但是 当我尝试使用 wsgi 服务器为我的应用程序提供服务时django core handlers wsgi WSGIHandle
  • python的shutil.move()在linux上是原子的吗?

    我想知道python的shutil move在linux上是否是原子的 如果源文件和目标文件位于两个不同的分区上 行为是否不同 或者与它们存在于同一分区上时的行为相同吗 我更关心的是如果源文件和目标文件位于同一分区上 shutil move
  • 通过Python连接到Bigquery:ProjectId和DatasetId必须非空

    我编写了以下脚本来通过 SDK 将 Big Query 连接到 Python 如下所示 from google cloud import bigquery client bigquery Client project My First Pr
  • 当字段是数字时怎么说...在 mongodb 中匹配?

    所以我的结果中有一个名为 城市 的字段 结果已损坏 有时它是一个实际名称 有时它是一个数字 以下代码显示所有记录 db zips aggregate project city substr city 0 1 sort city 1 我需要修
  • 将 Matlab 的 datenum 格式转换为 Python

    我刚刚开始从 Matlab 迁移到 Python 2 7 在读取 mat 文件时遇到一些问题 时间信息以 Matlab 的日期数字格式存储 对于那些不熟悉它的人 日期序列号将日历日期表示为自固定基准日期以来已经过去的天数 在 MATLAB
  • 找到一个数字所属的一组范围

    我有一个 200k 行的数字范围列表 例如开始位置 停止位置 该列表包括除了非重叠的重叠之外的所有类型的重叠 列表看起来像这样 3 5 10 30 15 25 5 15 25 35 我需要找到给定数字所属的范围 并对 100k 个数字重复该
  • 如何以正确的方式为独立的Python应用程序制作setup.py?

    我读过几个类似的主题 但还没有成功 我觉得我错过或误解了一些基本的事情 这就是我失败的原因 我有一个用 python 编写的 应用程序 我想在标准 setup py 的帮助下进行部署 由于功能复杂 它由不同的 python 模块组成 但单独
  • 如果 PyPy 快 6.3 倍,为什么我不应该使用 PyPy 而不是 CPython?

    我已经听到很多关于PyPy http en wikipedia org wiki PyPy项目 他们声称它比现有技术快 6 3 倍CPython http en wikipedia org wiki CPython口译员开启他们的网站 ht
  • 如何将 Django 中的权限添加到模型并使用 shell 进行测试

    我在模型中添加了 Meta 类并同步了数据库 然后在 shell 中创建了一个对象 它返回 false 所以我真的无法理解错误在哪里或者缺少什么是否在其他文件中可能存在某种配置 class Employer User Employer in
  • 将索引与值交换的最快方法

    考虑pd Series s s pd Series list abcdefghij list ABCDEFGHIJ s A a B b C c D d E e F f G g H h I i J j dtype object 交换索引和值并
  • pytest找不到模块[重复]

    这个问题在这里已经有答案了 我正在关注pytest 良好实践 https docs pytest org en latest explanation goodpractices html test discovery或者至少我认为我是 但是
  • 如何将Python3设置为Mac上的默认Python版本?

    有没有办法将 Python 3 8 3 设置为 macOS Catalina 版本 10 15 2 上的默认 Python 版本 我已经完成的步骤 看看它安装在哪里 ls l usr local bin python 我得到的输出是这样的
  • NLTK:查找单词大小为 2k 的上下文

    我有一个语料库 我有一个词 对于语料库中该单词的每次出现 我想获取一个包含该单词之前的 k 个单词和该单词之后的 k 个单词的列表 我在算法上做得很好 见下文 但我想知道 NLTK 是否提供了一些我错过的功能来满足我的需求 def size

随机推荐

  • TCP报文段的首部

    TCP报文段的首部 TCP虽然是面向字节流的 但是TCP传送的数据单元是报文段 一个TCP报文段分为首部和数据两部分 TCP报文段首部的前20个字节是固定 后面有 4 n 4n 4n字节是根据需要而增加的选项 n n
  • 基于优先队列的Dijkstra算法

    前言 最短路径问题 即在给定的连接图中 求解节点之间的最短路径 Dijkstra算法是典型的单源最短路径算法 单源即只能求解某个节点到其他节点的最短路径 另外 此算法不能处理边权重为负的情况 一 最短路径问题 最短路径问题是图论的一个经典算
  • ubuntu安装ffmpeg,三行命令

    1 安装 添加ppa源 sudo add apt repository ppa djcj hybrid 更新刚才添加的源 sudo apt get update 下载ffmpeg sudo apt get install ffmpeg 遇到
  • 【React】搭建React项目

    最近自己在尝试搭建react项目 其实react项目搭建没有想象中的那么复杂 我们只需要使用一个命令把React架子搭建好 其他的依赖可以根据具体的需求去安装 比如AntDesignMobile的UI框架 执行npm install ant
  • 绿源:“老大哥”冲刺IPO,新的故事如何讲?

    又一家老牌电动两轮车企业 开 向了资本市场 11月22日 绿源集团控股 开曼 有限公司 以下简称 绿源集团 正式向港交所递交招股说明书 拟主板挂牌上市 中信建设国际担任独家保荐人 这标志着 一部车骑10年 电动车品牌绿源拉开了上市序幕 绿源
  • Android暴露组件——被忽略的组件安全

    Intent 简介 Intent 意图 负责完成Android应用 组件之间的交互与通信 常见的Activity的调用 Receiver的发送 Service的启动都需离不开Intent Intent通常包含的信息 Categpry 种类
  • springboot文件上传 MultipartFile file

    1 讲解springboot文件上传 MultipartFile file 源自SpringMVC 注意点 如果想要直接访问html页面 则需要把html放在springboot默认加载的文件夹下面 MultipartFile 对象的tra
  • Yii Framework 开发教程(28) Data Provider 简介

    这开始介绍Zii组件之前 先简要介绍一下Yii支持的数据源接口 IDataProvider IDataProvider主要功能是为UI组件如GridView ListView等提供数据源 同时也支持数据的分页和排序 下图为Yii内置的三种数
  • QQxml和json代码生成卡片的方法

    简介 最近看到qq群里总有人发一些奇怪的卡片 例如下面这个卡片 点击之后就会跳转到你自己的个人资料 是不是很神奇 其实这是依靠xml代码转成的卡片 通过一些软件对xml编译执行 可以编译xml的软件有很多 最常用的手机xml编译执行软件是华
  • Android Display架构分析

    Fence https www jianshu com p 3c61375cc15b android12 display分析 https www cnblogs com roger yu p 15641545 html hwcomper h
  • MIPS汇编语言实现选择排序算法

    MIPS汇编语言实现选择排序算法 1 流程图 2 C代码 3 MIPS代码 附注释 MIPS汇编语言实现选择排序算法 1 流程图 2 C代码 include
  • c++笔记(一)

    这里写的主要是一些c c 值得注意的地方和c primer笔记 方便以后回顾 复习c 当然会有一些错误 发现后再改正 当形参引用时 数组不能转化为指针 是连接符 当宏定义用多行时常用 1 c中不可以连续赋值 c 可以 如int a b c
  • 数据结构课程设计---------最少换车次数问题

    问题描述 设某城市有n个车站 并有m条公交线路连接这些车站 设这些公交车都是单向的 这n个车站被顺序编号为0 n 1 编号程序 输入该城市的公交线路数 车站个数 以及各公交线路上的各站编号 实现要求 求得从站0出发乘公交车至站n一1的最少换
  • ‘git‘不是内部或外部命令,也不是可运行的程序或批处理文件。

    一 出现问题 git 不是内部或外部命令 也不是可运行的程序或批处理文件 出现这个问题主要是git的环境变量没有设置 二 解决问题 首先右键我的电脑点击属性 在点击高级系统设置 点击环境变量 在下面这栏点击path设置环境变量 添加这三个环
  • mysql 获取倒数第二_如何从MySQL中的表中获取倒数第二条记录?

    要获得MySQL中最后一个记录 即倒数第二个 之前的记录 您需要使用子查询 语法如下SELECT FROM SELECT FROM yourTableName ORDER BY yourIdColumnName DESC LIMIT 2 a
  • python opencv 二值化 计算白色像素点

    贴部分代码 usr bin env python coding utf 8 import cv2 import numpy as np from PIL import Image area 0 def ostu img global are
  • Flutter audioplayers使用小结

    简介 audioplayers是一个可以支持同时播放多个音频文件的Flutter的库 用法也是相当的简单 AudioPlayer audioPlayer new AudioPlayer await audioPlayer play url
  • SqlServer的varchar最大长度

    SqlServer的varchar最大长度是8000 总会遇到这种字符串截断问题 但是在给表字段长度添加时最好还是不要添加为max 能用varchar n 的话就不必要去要求varchar max 性能问题 项目上确实出现过问题 这个博主的
  • 1115 裁判机

    1114 全素日 有一种数字游戏的规则如下 首先由裁判给定两个不同的正整数 然后参加游戏的几个人轮流给出正整数 要求给出的数字必须是前面已经出现的某两个正整数之差 且不能等于之前的任何一个数 游戏一直持续若干轮 中间有写重复或写错的人就出局
  • python数据分析:用户消费情况数据分析

    本次分析数据介绍 数据为某奶茶店2018年1月 2019年6月的销售数据 共计69 659项数据 用户共计23 570名 数据集共4个字段 user id 用户id order id 购买日期 order prodect 购买产品数 ord