Python数据分析
1 淘宝母婴用品数据分析案例
1.1 数据介绍
数据来源:Baby Goods Info Data
1.2 字段介绍
婴儿信息数据 Tianchi_mum_baby.csv
字段 | 描述 | 补充 |
---|---|---|
user_id | 用户id | |
birthday | 出生日期 | 格式yyymmdd,例如2011.11.11 |
gender | 性别 | 0:女性,1:男性,2:未知 |
购买历史数据 Tianchi_mum_baby_trade_history.csv
字段 | 描述 | 补充 |
---|---|---|
item_id | 商品id | |
user_id | 用户id | |
cat_id | 商品二级分类 | |
cat1 | 商品一级分类 | |
property | 商品属性 | |
buy_mount | 购买数量 | |
day | 购买日期 | 格式yyymmdd,例如2011.11.11 |
1.3 数据分析
1.3.1 购买历史数据
1.3.1.1 数据导入
import numpy as np
import pandas as pd
from pandas import Series, DataFrame# 购买历史数据
trade_history_df = pd.read_csv('./(sample)sam_tianchi_mum_baby_trade_history.csv')
trade_history_df.head()
'''user_id auction_id cat_id cat1 property buy_mount day
0 786295544 41098319944 50014866 50022520 21458:86755362;13023209:3593274;10984217:21985... 2 20140919
1 532110457 17916191097 50011993 28 21458:11399317;1628862:3251296;21475:137325;16... 1 20131011
2 249013725 21896936223 50012461 50014815 21458:30992;1628665:92012;1628665:3233938;1628... 1 20131011
3 917056007 12515996043 50018831 50014815 21458:15841995;21956:3494076;27000458:59723383... 2 20141023
4 444069173 20487688075 50013636 50008168 21458:30992;13658074:3323064;1628665:3233941;1... 1 20141103
'''
1.3.1.2 数据清洗
- 商品属性字段property是由编号组成,无法具体分析,进行删除处理。
trade_history_df.drop(labels='property', axis=1, inplace=True)
trade_history_df.info()
'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 29971 entries, 0 to 29970
Data columns (total 6 columns):# Column Non-Null Count Dtype
--- ------ -------------- -----0 user_id 29971 non-null int641 auction_id 29971 non-null int642 cat_id 29971 non-null int643 cat1 29971 non-null int644 buy_mount 29971 non-null int645 day 29971 non-null int64
dtypes: int64(6)
memory usage: 1.4 MB
'''
- 将购买日期字段day数据转换成时间类型。
to_datetime方法用于将数据转换成时间类型。
pd.to_datetime(arg,format = None,...)
参数format:指定原始数据的格式。
trade_history_df['day'] = pd.to_datetime(trade_history_df['day'], format='%Y%m%d')
trade_history_df.head()
'''user_id auction_id cat_id cat1 buy_mount day
0 786295544 41098319944 50014866 50022520 2 2014-09-19
1 532110457 17916191097 50011993 28 1 2013-10-11
2 249013725 21896936223 50012461 50014815 1 2013-10-11
3 917056007 12515996043 50018831 50014815 2 2014-10-23
4 444069173 20487688075 50013636 50008168 1 2014-11-03
'''
- 检查是否存在空值
trade_history_df.isnull().any(axis=0)
'''
user_id False
auction_id False
cat_id False
cat1 False
buy_mount False
day False
dtype: bool
'''
- 检查购买数量字段buy_mount是否存在异常值
trade_history_df['buy_mount'].max() # 10000
trade_history_df['buy_mount'].min() # 1
(trade_history_df['buy_mount'] <= 0).sum() # 0
1.3.1.3 数据分析
- 查看数据的日期范围,显示出数据集的最早购买日期和最晚购买日期。
方式1:
df = trade_history_df.sort_values(by='day', axis=0, ascending=True)
df.head(1)['day'] # 20120702
df.tail(1)['day'] # 20150205
方式2:
trade_history_df['day'].min(), trade_history_df['day'].max()
# (Timestamp('2012-07-02 00:00:00'), Timestamp('2015-02-05 00:00:00'))
- 分析用户购买情况,判断用户趋向于多次购买商品还是只购买一次商品。
trade_history_df['user_id'].nunique() # 用户总数:29944(trade_history_df.groupby(by='user_id')['day'].count() == 1).value_counts()
'''
True 29919 # 只购买一次商品的用户数量
False 25 # 多次购买商品的用户数量
Name: day, dtype: int64
'''
1.3.2 婴儿信息数据
1.3.2.1 数据导入
user_df = pd.read_csv('./(sample)sam_tianchi_mum_baby.csv')
user_df.head()
'''user_id birthday gender
0 2757 20130311 1
1 415971 20121111 0
2 1372572 20120130 1
3 10339332 20110910 0
4 10642245 20130213 0
'''
1.3.2.2 数据清洗
- 将出生日期字段birthday数据转换成时间类型。
user_df['birthday'] = pd.to_datetime(user_df['birthday'], format='%Y%m%d')
- 检查是否存在缺失数据。
user_df.isnull().any(axis=0)
'''
user_id False
birthday False
gender False
dtype: bool
'''
- 处理性别字段中的异常数据。
user_df['gender'].value_counts()
'''
0 489
1 438
2 26
Name: gender, dtype: int64
'''
性别字段中数值2表示未知性别,这里被视为异常数据。
user_df = user_df.loc[~(user_df['gender'] == 2)]
user_df['gender'].value_counts()
'''
0 489
1 438
Name: gender, dtype: int64
'''
1.3.2.3 数据分析
查看男女比例
male_to_female_series = user_df['gender'].value_counts()
male_to_female_series[0] / male_to_female_series[1] # 1.1164383561643836
1.3.3 汇总购买历史数据和婴儿信息数据
1.3.3.1 汇总
total_df = pd.merge(trade_history_df_temp, user_df_temp, on='user_id', how='outer')
1.3.3.2 数据分析
- 查看用户复购程度
方式1
仅考虑购买次数,新用户只消费了一次,老用户消费了两次及以上。
(total_df.groupby(by='user_id')['day'].count() == 1).value_counts()
'''
True 29919 # 新用户
False 25 # 老用户
Name: day, dtype: int64
'''
方式2
考虑购买时间,如果用户的消费时间只有一条,则视为新用户,否则视为老用户。
获取用户消费时间的最大值和最小值,并判断这两个时间是否相等。相等则视为新用户,否则视为老用户。
groupby + agg
方法agg是对分组后的数据做进一步聚合操作。
max_min_day_dt = total_df.groupby(by='user_id')['day'].agg(['min', 'max'])
max_min_day_dt.head()
'''min max
user_id
2356 2013-05-11 2013-05-11
2757 2013-04-10 2013-04-10
3942 2013-07-14 2013-07-14
4468 2013-08-15 2013-08-15
7164 2014-11-11 2014-11-11
'''
(max_min_day_dt['min'] == max_min_day_dt['max']).value_counts()
'''
True 29920
False 24
dtype: int64
'''
- 添加新列,显示购买年份-月份。
total_df['month'] = total_df['day'].astype('datetime64[M]')
total_df['month'].head()
'''
0 2014-09-01
1 2013-10-01
2 2013-10-01
3 2014-10-01
4 2014-11-01
Name: month, dtype: datetime64[ns]
'''
- 查看每个月的商品销量情况,绘制线形图。
import matplotlib.pyplot as plt
%matplotlib inline sales_per_month_df = total_df.groupby(by='month')['buy_mount'].sum()plt.plot(sales_per_month_df)
# plt.plot(sales_per_month_df.index, sales_per_month_df.values)plt.xlabel('Month')
plt.ylabel('Sales')
plt.title('Sales per month')plt.xticks(rotation=30)
- 添加两个新列,分别显示购买的年和购买的月。
total_df['year_num'] = total_df['day'].dt.year
total_df['month_num'] = total_df['day'].dt.month
- 查看2012,2013,2014年每个月的销量情况,绘制线形图。
准备数据
方式1
sales12_df = total_df.loc[total_df['year_num'] == 2012]
sales12_per_month_series = sales12_df.groupby(by='month_num')['buy_mount'].sum()
sales13_df = total_df.loc[total_df['year_num'] == 2013]
sales13_per_month_series = sales13_df.groupby(by='month_num')['buy_mount'].sum()
sales14_df = total_df.loc[total_df['year_num'] == 2014]
sales14_per_month_series = sales14_df.groupby(by='month_num')['buy_mount'].sum()
方式2
sales_per_month_series = total_df.groupby(by=['year_num','month_num'])['buy_mount'].sum()
sales12_per_month_series = sales_per_month_series[2012]
sales13_per_month_series = sales_per_month_series[2013]
sales14_per_month_series = sales_per_month_series[2014]
plt.plot(sales12_per_month_series, label='2012')
plt.plot(sales13_per_month_series, label='2013')
plt.plot(sales14_per_month_series, label='2014')
plt.legend()
plt.xlabel('Month')
plt.ylabel('Sales')
plt.title('Sales per month')
1.3.3.3 针对性分析
从上图可以看出,2012年至2014年的5月,9月和11月都出现高峰凸起,整体呈现上涨趋势。
- 添加新列,显示购买的日期。
total_df['day_num'] = total_df['day'].dt.day
- 查看每年的5月,9月和11月中每天的销量情况。
# 5月
# 2012年没有5月的数据
sales13_05_series = total_df.query('month == "2013-05-01"')
sales14_05_series = total_df.query('month == "2014-05-01"')sales13_05_sum_series = sales13_05_series.groupby(by='day_num')['buy_mount'].sum()
sales14_05_sum_series = sales14_05_series.groupby(by='day_num')['buy_mount'].sum()plt.plot(sales13_05_sum_series, label='2013-05')
plt.plot(sales14_05_sum_series, label='2014-05')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Sales')
plt.title('Sales per day')
# 9月
sales12_09_series = total_df.query('month == "2012-09-01"')
sales13_09_series = total_df.query('month == "2013-09-01"')
sales14_09_series = total_df.query('month == "2014-09-01"')sales12_09_sum_series = sales12_09_series.groupby(by='day_num')['buy_mount'].sum()
sales13_09_sum_series = sales13_09_series.groupby(by='day_num')['buy_mount'].sum()
sales14_09_sum_series = sales14_09_series.groupby(by='day_num')['buy_mount'].sum()plt.plot(sales12_09_sum_series, label='2012-09')
plt.plot(sales13_09_sum_series, label='2013-09')
plt.plot(sales14_09_sum_series, label='2014-09')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Sales')
plt.title('Sales per day')
# 11月
sales12_11_series = total_df.query('month == "2012-11-01"')
sales13_11_series = total_df.query('month == "2013-11-01"')
sales14_11_series = total_df.query('month == "2014-11-01"')sales12_11_sum_series = sales12_11_series.groupby(by='day_num')['buy_mount'].sum()
sales13_11_sum_series = sales13_11_series.groupby(by='day_num')['buy_mount'].sum()
sales14_11_sum_series = sales14_11_series.groupby(by='day_num')['buy_mount'].sum()plt.plot(sales12_11_sum_series, label='2012-11')
plt.plot(sales13_11_sum_series, label='2013-11')
# plt.plot(sales14_11_sum_series, label='2014-11')
plt.legend()
plt.xlabel('Day')
plt.ylabel('Sales')
plt.title('Sales per day')
- 分析商品的一级分类销量情况,绘制柱状图。
cat1_sales_series = total_df.groupby(by='cat1')['buy_mount'].sum()cat1_sales_series.index
# Int64Index([28, 38, 50008168, 50014815, 50022520, 122650008], dtype='int64', name='cat1')
cat1_sales_series.index.astype('str')
# Index(['28', '38', '50008168', '50014815', '50022520', '122650008'], dtype='object', name='cat1')
plt.bar(cat1_sales_series.index.astype('str'), cat1_sales_series.values)
plt.xticks(rotation=30)
- 分析商品的一级分类与购买用户数量之间的关系,绘制柱状图。
注意,存在同一用户多次购买的情况。
如果不考虑同一用户多次购买的情况,对商品一级分类进行分组后,直接统计每个用户出现的次数(count)。如果某个一级分类商品被某位用户购买了多次,则会被视为多个用户购买了该一级分类商品,导致购买该一级分类商品的用户数量偏高。
# 不考虑同一用户多次购买的情况。
#
total_df.groupby(by='cat1')['user_id'].count()
'''
cat1
28 6963
38 1203
50008168 12494
50014815 4834
50022520 2367
122650008 2110
Name: user_id, dtype: int64
'''
考虑到同一用户多次购买的情况,需要对分组后的数据根据用户进行去重处理,再统计每个用户出现的次数,可以直接使用函数nunique。
# 考虑到同一用户多次购买的情况。
total_df.groupby(by='cat1')['user_id'].nunique()
'''
cat1
28 6958
38 1201
50008168 12484
50014815 4833
50022520 2367
122650008 2110
Name: user_id, dtype: int64
'''
因此需要考虑同一用户多次购买的情况。
cat1_user_count_df = total_df.groupby(by='cat1')['user_id'].nunique()
plt.bar(cat1_user_count_df.index.astype('str'), cat1_user_count_df.values)