数据分析实战:Shopee虾皮网销售数据分析

一、背景目标

Shopee(虾皮网)是东南亚电商平台,覆盖新加坡、马来西亚、菲律宾、泰国、越南、巴西、墨西哥、哥伦比亚、智利等十余个市场,触达超10亿消费者!2023年Shopee总订单量达82亿,23年Q4总订单数同比增长46%!

分析数据样本来自某爬虫系统爬取的Shopee网从2023年4月至2023年5月期间特定产品的销售数据。

任务要求

任务要求:从数据中获取在2023年5月上市的产品。

使用问题1中的清理数据来执行下一个任务。

1、显示每天爬取的产品数量。

2、显示基于位置的上市产品数量。你可以从“规格”字段中提取这个信息。

  • 如果它来自马来西亚的任何州,使用州名(如雪兰莪、柔佛、马六甲等)作为标签。
  • 如果它来自马来西亚之外的地方,使用“海外”作为标签。

  商品类别详情可能有如下格式:“Shopee | 女装 | 外套 | 大衣 & 夹克”。我们可以将其拆分为:

  • 主要类别:女装
  • 子类别 1:外套
  • 子类别 2:大衣 & 夹克

  a. 显示基于主要类别的上市产品数量。

  b. 对于前3个主要类别,根据产品数量显示该主要类别下的前5个子类别 1

3、显示每个主要类别的价格范围。

4、按降序显示每个主要类别的收入。

二、数据探索分析

2.1 数据概况

  • 数据时间范围: 2012年4月1日至2014年3月31日的数据

  • 数据记录数:20312行

  • 字段数:20个

  • 数据属性说明(字段)

字段含义
price_ori
价格
delivery
交付送达
item_category_detail 
项目类别详细信息
specification
规格
title
标题
w_date 
日期
link_ori
链接
item_rating 
评分
seller_name 
卖家名称
idElastic 
理想的
price_actual
当前价格
sitename 
站点名称
idHash
id的hash值
total_rating 
总评分
id
订单id,与idHash相同
total_sold
售出总数
pict_link 
图片链接
favorite
收藏数
timestamp
时间戳
desc
描述

三、数据预处理

3.1 重复值分析

# 查询是否有重复值
df.duplicated().sum()

3.2 缺失值分析

  • 检查每一列的缺失值,并降序排列

存在大量缺失值,但我们需要对缺失值字段注意分析,其中

delivery是交付送达情况,该值为空时,说明商品还未送达用户手中,属于正常情况;

specification、seller_name文本类型,且本次分析无需使用,可以忽略;

favorite是收藏数,观察数据,该字段是字符串型,为空时未有记录,不过可以将na填充为0

df['favorite'].fillna('0', inplace = True)

item_rating和total_rating字段是评分字段,且为空值记录数较小,将na填充为0

df['item_rating'].fillna('0', inplace=True)
df['total_rating'].fillna('0', inplace=True)

price_ori 和 price_actual字段是与价格相关的字段,这里需要进行缺失值的处理,处理方式是

  • 删除同时为空的记录
  • 删除同时为0的记录
  • 存在一个空字段的记录,这里用另外一个字段值填充
df = df.dropna(subset=['price_ori', 'price_actual'], how='all')
df = df[(df['price_ori'] != 0) & (df['price_actual'] != 0)].copy()
df['price_ori'].fillna(df['price_actual'], inplace=True)
df['price_actual'].fillna(df['price_ori'], inplace=True)

处理完成后的情况如下图所示:

3.3 新增字段

通过观察数据中specification字段的值,发现该字段均有 Ships From 子串,我们可以将此作为字符串分隔符,拆分出商品发货地,例如:

Product Specifications Category Shopee Men Clothes Traditional Wear Bottoms Stock 52 Ships From Mainland China

发货地为:Mainland China(中国大陆)

# 提取发货地
def extract_ship_from_spec(spec):
    if isinstance(spec, str):
        parts = spec.split('Ships From ')
        if len(parts) > 1:
            return parts[1:][0]
        else:
            return 'No state'  
    else:
        return 'No info'  

df['shipfrom'] = df['specification'].apply(extract_ship_from_spec)

四、数据分析

4.1 任务一

获取在2023年5月上市的产品,并获取每天爬取的产品数量。

df["w_date"] = pd.to_datetime(df["w_date"])
df_may2023 = df[( (df['w_date'].dt.year==2023) & (df['w_date'].dt.month==5) )]

df_crwal_bydate = df_may2023.groupby('w_date')['id'].count()

plt.figure(figsize=(10, 6)) 
plt.plot(df_crwal_bydate.index, df_crwal_bydate.values) 
plt.xlabel('爬取日期')
plt.ylabel('爬取产品数')
plt.title('每天爬取 Shopee 的产品')
plt.show()

4.2 任务二

显示基于位置的上市产品数量,通过观察数据,部分specification 中包含有 发货地信息,我们将从specification字段中截取发货地信息来进行分析

def extract_ship_from_spec(spec):
    if isinstance(spec, str):
        parts = spec.split('Ships From ')
        if len(parts) > 1:
            x = parts[-1].split()
            return x[-1]
        else:
            return 'No state'
    else:
        return 'No info'

# 将截取截取到信息保存在新增列shipfrom中
df_may2023.loc[:,'shipfrom'] = df_may2023['specification'].apply(extract_ship_from_spec)

# 根据任务要求,马来西亚的州名作为标签,其他全部以Oversea表示,这里我做了调整,将中国内地和中国台湾作为标签,其他以Oversea表示。

replacements = {
    'Taiwan': 'Taiwan China',
    'China': 'Mainland China',
    'Indonesia': 'Oversea',
    'Thailand': 'Oversea',
    'Vietnam': 'Oversea',
    'Korea': 'Oversea',
    'ID': 'Oversea',
    'No state': 'Oversea',
    'No info': 'Oversea'
}
df_may2023.loc[:,'shipfrom'] = df_may2023['shipfrom'].replace(replacements)

df_shipfrom_count = df_may2023['shipfrom'].value_counts().sort_values(ascending=True)

plt.figure(figsize=(14, 8))  
df_shipfrom_count.plot(kind = 'barh', color='skyblue', edgecolor='black', title = '2023年5月产品数量统计(by发货地)')
plt.xlabel('产品数量')
plt.ylabel('发货地')

for i in range(len(df_shipfrom_count)):
    plt.text(df_shipfrom_count.iloc[i], i, f'{df_shipfrom_count.iloc[i]}', ha='left', va='center')
    
plt.show()

4.3 任务三

拆分出商品主要类别、子类别 1、子类别 2

def extract_main_category(category_detail, level):
    if pd.isna(category_detail):
        return -1
    categories = category_detail.split('|')
    categories = [category.strip() for category in categories[1:]]

    if level <= 0 or level > len(categories):
        raise ValueError("请求的级别不合法或超出了分类的总数")
        return -2
        
    if len(categories) > 0:
        return categories[level - 1]
        
df_may2023.loc[:, 'Main_Category'] = df_may2023['item_category_detail'].apply(extract_main_category, level=1)
df_mainCatagory_count = df_may2023['Main_Category'].value_counts()
df_mainCatagory_count

a) 主要分类及产品数量

b)前3个主要类别,根据产品数量显示该主要类别下的前5个子类别 1

main_top3 = df_mainCatagory_count.nlargest(3)
for catagory in main_top3.index:
    catagory_data = df_may2023[ df_may2023['Main_Category'] == catagory]
    catagory_1_count = catagory_data['item_category_detail'].apply(extract_main_category, level=2).value_counts().nlargest(5)
    print(catagory_1_count)

结果如下

4.4 任务四

显示每个主要类别的价格范围

这里特别说一下,通过min、max、均值和中位数获得的结果显示,价格数据质量很差,无论采用哪个值都将存在很大的偏差。

df_price_range = df_may2023.groupby('Main_Category')['price_actual'].agg(['min','mean','median','max']).reset_index()
df_price_range.columns=['Main_Category','最低价','均价','中位数','最高价']
for column in ['最低价', '均价','中位数', '最高价']:
    df_price_range[column] = df_price_range[column].map('{:,.2f}'.format)
df_price_range

整体上来看,均价数值相差量级,中位数的数据相对合理,所以这里采用中位数来绘制条形图。

4.5 任务五

按降序显示每个主要类别的收入。

df_revenue_category = df_may2023.groupby('Main_Category')['price_actual'].sum() \
    .sort_values(ascending=False) \
    .reset_index() \
    .rename(columns={'price_actual': '收入'})
df_revenue_category

五、小结

本次项目任务明确,在分析过程中,数据处理环节较为重要,注意事项总结如下:

1、对于价格字段的处理,实际价格和标价都存在空值,所以需要进行相应的处理

  • 2个字段全部为空或全部为0的记录,删除
  • 2个字段中有1个为空时,用另一个字段值填充

2、遇到了排序无效的问题,初步排查原因是价格字段中有表示货币的数值格式(即带有千位分隔符,),进行排序时乱序排列,解决办法是将类型转换为字符串后,删除逗号再转换回float类型。

3、尽量使用 .loc 来进行行和列的索引,以避免SettingWithCopyWarning 警告

4、发货地部分,采用了最粗暴的方法,若仔细观察数据,实际上是可以继续清洗,以获得更好的数据结果。当然,由于数据是文本型,商家在填写specification时也并非规范,没有必要问题复杂化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值