Python大数据分析8:案例-我们还是像以前一样喜欢音乐器材商品吗?

这次我们准备使用一个更大规模的数据来看看时间分析的方法应用。利用人们购物信息中的时间信息,可以从时间演化的角度来观察这些年人们购物的兴趣变化。

Amazon电子商务平台是一个著名的国际电商平台,其中它也提供了近一份不错的数据资源,共计包含1.4亿件商品评论,时间跨度从1996年5月直到2014年7月。我们可以从这个学者提供的网站来免费下载使用。由于数据量太大,我们可以选择部分类别的数据来做分析。

比如我们今天只选择音乐器材类商品的相关数据,具体位置位于文件列表的倒数第二个,选择这个评分文件就可以了。

这个文件是个CSV格式的文本文件,对于以后我们处理的各种数据,建议大家不要随意使用双击的默认形式来打开,比如此类文件默认会使用Excel打开,但是文件可能更大,有时会导致打开失败。因此这里建议大家安装免费的Sublime专业文本编辑器,同学可以在网上直接搜索下载安装。

这里我们打开了,可以看到里面有四列,和以前电影数据MovieLens很相似,是用户ID、商品ID、评分和时间戳信息。

我们首先来读取下,大家可以参考以前介绍的方法,仍然使用pandas的read_csv方法:

rnames = [‘uid’, ‘pid’, ‘rating’, ‘timestamp’]

ratings = pd.read_csv(‘C:\\temp\\ratings_Musical_Instruments.csv’, header=None, names=rnames)

其中我们读取了文件,同时还指定了四个列的名称。通过输出观察,数据加载没有问题。

由于我们需要进行时间分析,因此接下来重点要放到时间信息上。这里的时间信息为时间戳,什么是时间戳?时间戳其实是一个整数,说的专业一些就是利用与1970年1月1日0点之间总共的秒数来表达当前时间,显然这个值越大,说明离1970年初那个时间点越远。虽然简单,还是并不好用。

我们需要将其转换为我们熟悉的datetime类型。

ratings[‘date’] = ratings[‘timestamp’].apply(datetime.fromtimestamp)

ratings[‘year’] = ratings[‘date’].dt.year

ratings[‘month’] = ratings[‘date’].dt.month

这里我们首先利用datetime类型的fromtimestamp函数就可以将每一个时间戳变成datetime,仍然还是利用apply函数来完成。然后,将时间保存到现有的DataFrame中,并且为了后续分析的方便,我们还单独保存了提取的年份和月份信息。

我们先来热热身。比如看看这些多年,人们对于音乐器材类商品的评价感受发生了什么变化,我们以年份为分组单位,统计每个年份的评分平均值。

print(ratings[‘rating’].groupby(ratings[‘year’]).mean())

我们准备使用matplotlib绘制下。为了完成这个目标,我们需要做两方面的准备:

第一:要将分组后的结果保存下来,一般分组列自动设置为索引,在绘制时会被设置为横轴,绘制内容选择分组统计的列,在绘制时会设置为纵轴。

results = ratings[[‘rating’]].groupby(ratings[‘year’]).mean()

print(results)

第二:引入并补齐前面介绍的matplotlib绘制方法。

import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt

rnames = ['uid', 'pid', 'rating', 'timestamp']
ratings = pd.read_csv('C:\\temp\\ratings_Musical_Instruments.csv', header=None, names=rnames)
ratings['date'] = ratings['timestamp'].apply(datetime.fromtimestamp)
ratings['year'] = ratings['date'].dt.year
ratings['month'] = ratings['date'].dt.month
results = ratings[['rating']].groupby(ratings['year']).mean()
plt.figure(figsize=(8, 4), dpi=100)
plt.plot(results['rating'])
plt.show()

通过结果可以看到,在17年的时间中,评分数据呈现出一种明显的向下凹陷的状态,其中最低点在2003年左右。个中原因我们可以自行去结合其他数据去思考可能的原因,后续课程我们也会通过相关性方法来介绍一些可行的分析途径。

同时,由于数据量足够,我们甚至都可以看看每个月大家的评分感受及其波动情况。

print(ratings[[‘rating’]].groupby(ratings[‘month’]).mean())

这里我们把year分组条件换成了month。

同样也可以使用matplotlib绘制下。

情况似乎很不一样,最高分都出现在夏季,并且有两个明显的高峰,但是随后就快速下降,并只在10月份出现一次小的上升。

也可以结合年月进行综合分组:

print(ratings[[‘rating’]].groupby([ratings[‘year’], ratings[‘month’]]).mean())

但是这里由于分组是两个列,因为无法绘制成一个维度,因此必须使用一个唯一的列来绘制。我们这次利用前文介绍的时间转换方法来试下。

为此,首先将时间信息直接设置为索引,然后就可以利用to_period方法转换为月份信息:

ratings = ratings.set_index(‘date’)

ratings = ratings.to_period(freq=’M’)

print(ratings)

然后就可以分组了。

results = ratings[[‘rating’]].groupby(ratings.index).mean()

print(results)

最后就可以实现绘制。

import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt

rnames = ['uid', 'pid', 'rating', 'timestamp']
ratings = pd.read_csv('C:\\temp\\ratings_Musical_Instruments.csv', header=None, names=rnames)
ratings['date'] = ratings['timestamp'].apply(datetime.fromtimestamp)
ratings = ratings.set_index('date')
ratings = ratings.to_period(freq='M')
results = ratings[['rating']].groupby(ratings.index).mean()
plt.figure(figsize=(8, 4), dpi=100)
plt.plot(results.index.to_timestamp(), results['rating'])
plt.show()

这里注意一个非常重要的地方,就是转换后的索引为Periodindex类型,无法在参与绘图时表达横轴,因此还需再次转换为时间类型,如利用to_timestamp方法。

明显这个结果更为细致,波动更为明显。虽然这种波动过于细致,使得整体性趋势看的不是很明显。

作为对比,我们就会发现细致的结果更清晰,比如按照年度显示,2013年平均评分最低,但是其实按照年月进行,其实是2013年11月呈现最低点。

但是,仅仅考虑平均评分并不能充分说明问题,事实上,每个时间点上的评分有效数量也非常关键。因此我们可以再次通过分组汇总评分数量来看看情况。

results = ratings.groupby(ratings.index)[[‘rating’]].count()

我们只修改了刚才语句中的mean。

我们发现一个明显的事实,那就是购买音乐器材类商品的人在2010年以后获得了极大的增长,在2010前之前几乎都是关注度不高,但是到2014年居然增加了接近10倍。

因此对比来看,这可能从一个方面解释为什么平均评分为什么早期波动较大,因为购买人数越多,数据的整体稳定性就越强。另外,在购买人数发生较大增长的情况下,平均评分还在微弱上升,更能说明2010年喜欢自己学习演奏音乐的人也是越来越多。

但是,上述分析有一个并不理想的地方,就是我们需要绘制两张不同的图来分别查看,能否通过一张图来展示呢?

这张图首先需要两组数据,一个是平均评分,一个是评分个数,然后还需将这两组数据表现在图形上,于是就有一个问题:该如何表现?其实方法很多,下一章我们重点专门说明。这里可以考虑使用一个散点图来试一下。

我们先做第一步,得到两个分组统计数据。前文已经介绍,可以使用agg函数来进行。

results = ratings[[‘rating’]].groupby(ratings.index).agg([‘mean’, ‘count’])

print(results)

这种方法用于看分组数据结果没有问题,主要是统计后的列没有一个独立的名称。

如果后续需要读取特定的统计列,直接写出单一列名称会出错:

print(results[‘count’])

显然这也会导致后续绘图时无法获取指定的统计列。

解决方法很多,我们先看第一种情况:

可以使用两个层次结构的列名来限定指定统计列:

print(results[‘rating’][‘count’])

数据访问没有问题。

那么如何绘制散点图呢?很简单,将plot(线性图)换成scatter(散点图),此时除了指定时间索引外,还需指定两个数据,分别是随着时间横轴移动的平均评分数据,和每个时间点上的评分总数数据。

line = plt.scatter(results.index.to_timestamp(), results[‘rating’][‘mean’], results[‘rating’][‘count’])

plt.show()

图中既能通过时间维度变化看出平均评分的变化趋势,还能通过点的大小看出相关评分数量的影响。

当然,你如果将平均评分和评分总数互相交换下,就能得到另一种分析的角度:

line = plt.scatter(results.index.to_timestamp(), results[‘rating’][‘count’], results[‘rating’][‘mean’])

plt.show()

效果不明显,主要原因在于平均评分在当前这个以评分总数来测度的数据范围内,差异度不大。

为此可以考虑乘以1000,增加显示效果。这就需要大家动动脑筋,看看能不能拉大显示数据的差异度。

这张图明显拉大了距离,虽然拉的比例未必符合最初的状态,但是对于直观感受数据的变化有时是必要的。

这里不做要求,有兴趣的同学可以再试一下新的方法。比如这里我们采用的方法是首先将现有的取值映射到0到1之间,然后乘以30后平方,以放大数据彼此之间的差异。

results[‘newAvg’] = 30 * (results[‘avg’] – results[‘avg’].min()) / (results[‘avg’].max() – results[‘avg’].min())

results[‘newAvg’] = results[‘newAvg’] * results[‘newAvg’]

回到刚才的分组后两个列的名称上来看,其实还有两种解决方法。

results = ratings.groupby(ratings.index).agg(avg=(‘rating’, ‘mean’), cnt=(‘rating’, ‘count’))

print(results[‘avg’])

这里的agg中既指定了要对哪个列进行什么聚合运算,还能指定新的列名称。

此时就可以通过这个新列名称获取相应的统计信息:

还有一种更为简单,改变要聚合的列书写的位置,此时即可直接使用聚合运算的名称来获取特定列的数据:

results = ratings.groupby(ratings.index)[‘rating’].agg([‘mean’, ‘count’])

print(results[‘mean’])

当然,如何把图形做的更漂亮,这里我们使用了颜色主题和透明度,效果比一开始的样子要好看多了,很难吗?其实很简单,就多了两个设置的两行语句,我们下一章将专门介绍。请给大家继续学习。

发表评论

邮箱地址不会被公开。 必填项已用*标注