分析实例: 淘宝消费者行为分析

  • 本文侧重用 Hive SQL 处理数据

1. 数据集说明

这次分析用的数据是来自阿里云天池:
数据集来源:user_behavior
数据采用csv格式保存, 未解压前905MB, 解压后3.41G

数据集包含了 2017 年 11 月 25 日至 2017 年 12 月 3 日之间,有行为的约一百万随机用户的所有行为(行为包括点击、购买、加购、喜欢)。数据集的组织形式和 MovieLens-20M 类似,即数据集的每一行表示一条用户行为,由用户 ID、商品 ID、商品类目 ID、行为类型和时间戳组成,并以逗号分隔。关于数据集中每一列的详细描述如下:

列名称 说明
用户 ID 整数类型,序列化后的用户 ID
商品 ID 整数类型,序列化后的商品 ID
商品类目 ID 整数类型,序列化后的商品所属类目 ID
行为类型 字符串,枚举类型,包括 ('pv', 'buy', 'cart', 'fav')
时间戳 行为发生的时间戳

注意到,用户行为类型共有四种,它们分别是

行为类型 说明
pv 商品详情页 pv,等价于点击
buy 商品购买
cart 将商品加入购物车
fav 收藏商品

2. 数据处理思路, 整个过程以 Hive 为主

  • 使用pandas做数据的初步处理, 只取日期 2017 年数据, 只取部分数据
  • 导入 hive 清洗数据
  • Hive 查询数据做分析

3. 数据处理

3.1 pandas 初步处理, 简单清洗

数据量过大, 导入 pandas 时分块
分块导入用 pd.read_csv 参数 chunksize, 以50万行为一块

import pandas as pd
data_path = r'D:\Downloads\Compressed\UserBehavior.csv'
chunks = pd.read_csv(data_path, chunksize=500000)

3.2 取 39块*50万条=1950万条 里, 满足条件 year==2017的数据

  • 为了后续筛选方便, 指定了列名
  • 后续 Hive 导入数据时一般没有表头, 所有导出时 header 参数设为 False
  • 筛选后保存为 csv 时, 用 mode='a', 追加模式, 否则只有最后一块的数据
i = 1
for chunk_i in chunks:
    chunk_i.columns = ['uid', 'itemid', 'catid', 'behavior', 'timestamp']
    chunk_i['date'] = pd.to_datetime(chunk_i['timestamp'], unit='s').dt.date   # dt. 是 pandas 中和时期相关的函数
    chunk_i = chunk_i[pd.to_datetime(chunk_i['timestamp'], unit='s').dt.year==2017]
    chunk_i.to_csv(r'D:\Downloads\Compressed\2017_1.csv', mode='a', encoding='utf-8', header=False, index=False)  # a 模式是追加
    print('已完成第{}个chunk!!!)'.format(str(i)))
    i += 1
    if i >=40:
        break

3.3 Hive 建表

  • 不能直接用 timestamp, datetime 做为字段名, 查询会报错
drop table if exists user_behavior;
create table user_behavior
    (
        `user_id` string comment '用户ID',
        `item_id` string comment '商品ID',
        `category_id` string comment '商品类目ID',
        `behavior_type` string  comment '行为类型,枚举类型,包括(pv, buy, cart, fav)',
        `time_stamp` int comment '行为时间戳',
        date_time string comment '行为时间'
    )
row format delimited
fields terminated by ','
lines terminated by '\n';

加载数据

LOAD DATA LOCAL INPATH '/opt/module/hivedata/2017_1.csv' OVERWRITE INTO TABLE user_behavior;

3.4 数据清洗

  • 删除重复数据
    • 全字段分组方法
insert overwrite table
  user_behavior
select
  user_id,
  item_id,
  category_id,
  behavior_type,
  time_stamp,
  date_time
from user_behavior
group by user_id, item_id, category_id, behavior_type, time_stamp, date_time;

查看去重前后数据条数 前:19499724 后:199499716

select count(*) from user_behavior;
  • 查看时间是否有异常值
select date(date_time) as day from user_behavior group by date(date_time) order by day;

结果:


image.png

可以看到 11.01 至 12.06 这部分日期是连续的

  • 选取 11.01 至 12.06 部分数据
insert overwrite table 
    user_behavior
select
    user_id,
    item_id,
    category_id,
    behavior_type,
    time_stamp,
    date_time
from user_behavior
where cast(date_time as date) between '2017-11-01' and '2017-12-06';

查看条数: 19499554, 只比之前少了约 50 万条, 上面的截取是合理的

select count(*) from user_behavior;
OK
_c0
19499554
Time taken: 0.078 seconds, Fetched: 1 row(s)

4. 数据分析

4.1 用户流量及购物情况

4.1.1 总访问量PV,总用户量UV

select
    sum
    (
        case when behavior_type = 'pv' then 1 else 0 end
    ) as PV,
    count(distinct user_id) as UV
from user_behavior;
result:
pv      uv
17462837        193072
Time taken: 12.062 seconds, Fetched: 1 row(s)

4.1.2 日访问量, 日访客量

select
    cast(date_time as date) as day,
        sum
    (
        case when behavior_type = 'pv' then 1 else 0 end
    ) as PV,
    count(distinct user_id) as UV
from user_behavior
group by cast(date_time as date)
order by day;

4.1.3 每个用户的购物情况, 加工到表 user_behavior_count

  • 建表: 每个用户ID的点击数, 收藏数, 加购物车数, 购买数
drop table if exists user_behavior_count;
create table
    user_behavior_count
as
select
    user_id,
    sum(case when behavior_type = 'pv' then 1 else 0 end) as pv,
    sum(case when behavior_type = 'fav' then 1 else 0 end) as fav,
    sum(case when behavior_type = 'cart' then 1 else 0 end) as cart,
    sum(case when behavior_type = 'buy' then 1 else 0 end) as buy
from user_behavior
group by user_id

4.1.4 复购率: 产生两次以上购买的用户占总购买用户的比例

select round(sum(case when buy > 1 then 1 else 0 end) / sum(case when buy > 0 then 1 else 0 end),2) as repur_rate
from user_behavior_count;
# 结果
repur_rate
0.66
Time taken: 3.885 seconds, Fetched: 1 row(s)

复购率 0.65, 一个月加6天的复购率相当高, 可以看到数据并不真实

4.2 用户行为转换率

  • 点击/(购物车+收藏)/购买, 各环节转化率
select
    t1.pv,
    t1.fav,
    t1.cart,
    t1.buy,
    round((t1.fav+t1.cart)/t1.pv,2) as pv2favcart,
    round(t1.buy/(t1.fav+t1.cart),2) as favcart2buy,
    round(t1.buy/t1.pv,2) as pv2buy
from
    (
        select
            sum(pv) as pv,
            sum(fav) as fav,
            sum(cart) as cart,
            sum(cart) as buy
        from user_behavior_count
    ) t1
# 结果
t1.pv         t1.fav    t1.cart  t1.buy       pv2favcart   favcart2buy    pv2buy
17462837        561876  1080448 1080448      0.09              0.66      0.06

4.3 用户行为习惯

4.3.1 一天的活跃时段分布

  • 注意用 from_unixtime 把时间戳转化为 datetime, 再用 hour() 取出小时数
  • from_unixtime 不指定参数的话返回的是标准日期时间格式 '2017-11-29 07:36:11'
select
    hour(from_unixtime(time_stamp)) as hour,
    sum(case when behavior_type='pv' then 1 else 0 end) as pv,
    sum(case when behavior_type='fav' then 1 else 0 end) as fav,
    sum(case when behavior_type='cart' then 1 else 0 end) as cart,
    sum(case when behavior_type='bay' then 1 else 0 end) as buy
from user_behavior
group by hour(from_unixtime(time_stamp))
order by hour asc;
# 结果:
hour    pv      fav     cart    buy
0       594554  20300   37359   0
1       727700  25338   45161   0
2       843907  29117   52355   0
3       821918  28716   50895   0
4       827374  27341   50162   0
5       908718  29583   54490   0
6       906076  28846   53930   0
7       937116  29168   55822   0
8       899795  29128   53910   0
9       813879  26529   49851   0
10      836029  25335   49278   0
11      1061073 31121   61742   0
12      1281650 37073   76848   0
13      1467984 43016   90998   0
14      1447740 44848   94745   0
15      1094537 37631   77633   0
16      594787  20715   35773   0
17      276324  9879    16967   0
18      152075  5576    9190    0
19      101803  3738    6488    0
20      87890   2919    5493    0
21      112117  3708    7232    0
22      236643  7812    15696   0
23      431148  14439   28430   0
Time taken: 16.276 seconds, Fetched: 24 row(s)

4.3.2 一周用户的活跃分布

  • pmod(int a, int b) pmod(double a, double b) 返回 a 除 b 的余数的绝对值
  • 低版本: Hive2.2.0前 没有dayofweek, 在些之前的实现方式 pmod(datediff('${date}', '1920-01-01') - 3, 7)
  • 低版本写法:
select 
    pmod(datediff(date_time, '1920-01-01') - 3, 7) as weekday,
    sum(case when behavior_type = 'pv' then 1 else 0 end) as pv,   --点击数
    sum(case when behavior_type = 'fav' then 1 else 0 end) as fav,  --收藏数
    sum(case when behavior_type = 'cart' then 1 else 0 end) as cart,  --加购物车数
    sum(case when behavior_type = 'buy' then 1 else 0 end) as buy  --购买数
from user_behavior
where date(date_time) between '2017-11-27' and '2017-12-03'
group by pmod(datediff(date_time, '1920-01-01') - 3, 7)
order by weekday;
# 结果
weekday pv      fav     cart    buy
0       2094177 66600   132406  46351
1       1745630 55989   105415  42242
2       1722171 56017   104236  41420
3       1791741 58662   108235  43361
4       1835891 58967   111709  43701
5       1949782 60964   125542  42104
6       2438566 79313   157069  51231
Time taken: 11.368 seconds, Fetched: 7 row(s)
  • dayofweek 写法
select
    dayofweek(date_time) as weekday,
    sum(case when behavior_type = 'pv' then 1 else 0 end) as pv,
    sum(case when behavior_type = 'fav' then 1 else 0 end) as fav,
    sum(case when behavior_type = 'cart' then 1 else 0 end) as cart,
    sum(case when behavior_type = 'buy' then 1 else 0 end) as buy
from user_behavior
group by dayofweek(date_time)
order by dayofweek(date_time);
# 结果
weekday pv      fav     cart    buy
1       1745764 55989   105415  42242
2       1722338 56017   104236  41420
3       1792162 58662   108235  43361
4       1837044 58967   111709  43701
5       2164021 67940   137930  45275
6       4269135 137972  267528  90002
7       3932373 126329  245395  88392
Time taken: 9.751 seconds, Fetched: 7 row(s)

4.4 基于 RFM 模型找出有价值的用户

  • 近度 R-Rencecy 最近一次购买时间
  • 频率 F-Frequency 消费频率
  • M-Money 消费金额
  • R R_rank 值越小, 用户越活跃
  • 这里的窗口函数是用来添加排名的
  • RANK() 排序相同时会重复,总数不会变. 如果第1名重复的时候就没有第2名,1,1,3,4...
  • dense_rank 排序相同时会重复,总数会减少. 如果第1名重复的时候, 1,1,2,3...
  • ROW_NUMBER() 会根据顺序计算. 重复位置也排名, 第1名和第2名分数相同时, 1,2,3,4...
  • datediff(date_1, date_2) date_1 减去 date_2
  • R
select
    user_id,
    datediff('2017-12-06', max(date_time)) as R,
    dense_rank()over(order by datediff('2017-12-06', max(date_time)) ASC) as R_rank
from user_behavior
where behavior_type='buy'
group by user_id
limit 50;
  • F
select
    user_id,
    count(1) as F,
    dense_rank()over(order by count(1) DESC) as F_rank
from user_behavior
where behavior_type='buy'
group by user_id
limit 50,
  • 对有购买行为的用户按照排名进行分组,共划分为 5 组,
    • 前 - 1/5 的用户打 5 分
    • 前 1/5 - 2/5 的用户打 4 分
    • 前 2/5 - 3/5 的用户打 3 分
    • 前 3/5 - 4/5 的用户打 2 分
    • 前 4/5 - 的用户打 1 分
  • 按照这个规则分别对用户时间间隔排名打分和购买频率排名打分,最后把两个分数合并在一起作为该名用户的最终评分
with t1 as
    (
        select
            user_id,
            datediff('2017-12-06', max(date_time)) as R,
            dense_rank()over(order by datediff('2017-12-06', max(date_time)) ASC) as R_rank,
            count(1) as F,
            dense_rank()over(order by count(1) DESC) as F_rank
        from user_behavior
        where behavior_type='buy'
        group by user_id
    )

select
    user_id,
    R,
    R_rank, 
    R_score,
    F,
    F_rank,
    F_score,
    R_score+F_score as score
from
    (
        select
            *,
            case ntile(5) over(order by R_rank)
                when 1 then 5
                when 2 then 4
                when 3 then 3
                when 4 then 2
                when 5 then 1
            end as R_score,
            case ntile(5) over(order by F_rank)
                when 1 then 5
                when 2 then 4
                when 3 then 3
                when 4 then 2
                when 5 then 1
            end as F_score
        from t1
    ) as t2
order by score desc
limit 20
# 结果
user_id r       r_rank  r_score f       f_rank  f_score score
702034  3       1       5       159     1       5       10
562220  3       1       5       4       59      5       10
490508  3       1       5       87      3       5       10
337305  3       1       5       93      2       5       10
107932  3       1       5       72      5       5       10
754360  3       1       5       71      6       5       10
834051  3       1       5       69      7       5       10
705488  3       1       5       4       59      5       10
381745  3       1       5       45      19      5       10
705460  3       1       5       4       59      5       10
128379  3       1       5       65      10      5       10
914956  3       1       5       43      20      5       10
675514  3       1       5       61      12      5       10
190873  3       1       5       61      12      5       10
242650  3       1       5       60      13      5       10
122504  3       1       5       69      7       5       10
9610    3       1       5       67      8       5       10
858271  3       1       5       53      16      5       10
165222  3       1       5       51      17      5       10
549770  3       1       5       66      9       5       10
Time taken: 14.839 seconds, Fetched: 20 row(s)
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容