Python大数据分析6:案例-什么样的顾客买的更多?

这里我们使用一个著名的数据集合,是kaggle网站提供的某超市销售记录,共有近54万条记录,

每条记录都给出了在黑色星期五(国外著名的年终购物节)购买商品的顾客信息,其中含有诸如年龄、职业、地区和购物额等基本信息,我们不妨利用这个数据来看看可视化分析的效果。

首先,我们加载数据集,并统计不同年龄段上购物额的平均值:

# coding:utf-8

import pandas as pd

frame = pd.read_csv(‘C:\\temp\\BlackFriday.csv’)

results = frame[[‘Purchase’]].groupby(frame[‘Age’]).mean()

print(results)

直接看结果,我们可以发现随着年龄的增长,购物额总体呈现不断上升的趋势。

使用散点图,可以更好的看出对应趋势:

plt.scatter(results[‘Purchase’], results.index, c=results[‘Purchase’], s=results[‘Purchase’])

plt.colorbar()

plt.show()

这里还故意使用颜色和点的大小来表达购物额的大小。

我们再看看居住时长的影响。

results = frame[[‘Purchase’]].groupby(frame[‘Stay_In_Current_City_Years’]).mean()

plt.scatter(results[‘Purchase’], results.index, c=results[‘Purchase’], s=results[‘Purchase’])

plt.colorbar()

plt.show()

也呈现出类似的效果。

使用三维散点图也可以进一步综合来看:

ax = plt.subplot(projection=’3d’)

ax.scatter(results[‘Purchase’], results.index.codes[0], results.index.codes[1],

           color=cm.ScalarMappable().to_rgba(results[‘Purchase’]), s=200)

sm = plt.cm.ScalarMappable(norm=plt.Normalize(vmin=results[‘Purchase’].min(), vmax=results[‘Purchase’].max()))

plt.colorbar(sm)

plt.show()

发现确实购物额会随着年龄段和居住时长的增长而增多。

这样做没有问题,但是这种分析过于麻烦,需要自己一个个的去分析总结。而且这里的问题其实表现为一种数据相关性问题,就是用户什么样的特征属性与购物额有重要的联系?通过分组查询,确实能在总体上看出数据之间的联系,但是一般要求这种数据是数值类型,如果是一般的文本描述,则分组查询就难以利用分组条件属性的数值变化关系看出来对应的统计属性的变化相关性。

因此,我们可以尝试使用更为专业的数据相关度分析方法:

DataFrame的corr函数可以返回数据相关度,默认方法为皮尔逊系数,该数值为一个-1到1之间的小数,越靠近1,说明正相关,越靠近-1说明负相关,靠近0说明不相关。

pd.set_option(‘display.max_columns’, None)

print(frame.corr())

为了看清全部列,我们增加了显示全部列的设置。从结果可以看出,自己列和自己列肯定是完全相关,没有意义。其中最高的是产品类别1和产品类别2、产品类别3和产品类别2,这两个数据如果借助分组查询来分析,由于相关数值都是都是类别标签代码,数值之间的大小不能反映任何联系,只用于区分,因此很难直接看出。

不过,我们刚才计算的年龄和居住时长却没有出现。

原因在于这两个列都不是数值型,一个是年龄范围的表示,一个部分记录是非整数型。因此我们必须对数据进行必要的处理。

我们先看年龄列。为了简单处理,这一列我们可以只取数据的第一个数字,既能反映年龄区间,也是整数。至于如何取第一个字符,其实很简单,

print(‘Age'[0])

这就表示取A这个第一个字符,但是对于DataFrame来说,这样写会被误解为行的表示:

print(frame[‘Age’][0])

因此我们采用了apply方法:

print(frame[‘Age’].apply(lambda x: x[0]))

大家可能觉得apply里面这个看不懂,这其实是个匿名函数,大家也虚无了解细节,只需了解通过这种写法,每个Age数据被apply函数通过x传递到[0]上,形成正确的表示。

我们再看看居住时长,只需将4+这种表示的全部映射为4即可。

frame.loc[frame[‘Stay_In_Current_City_Years’] == ‘4+’, ‘Stay_In_Current_City_Years’] = 4

print(frame)

这其实是个查询更新问题,即找到所有居住时长为4+的记录,并将这些记录的居住时长更新为4。

最终我们就可以完成更多列的相关度分析了。

# coding:utf-8
import pandas as pd

frame = pd.read_csv('C:\\temp\\BlackFriday.csv')
pd.set_option('display.max_columns', None)
frame['Age'] = frame['Age'].apply(lambda x: x[0])
frame.loc[frame['Stay_In_Current_City_Years'] == '4+', 'Stay_In_Current_City_Years'] = 4
frame['Age'] = frame['Age'].astype(int)
frame['Stay_In_Current_City_Years'] = frame['Stay_In_Current_City_Years'].astype(int)
print(frame.corr())

这里要注意一个问题,必须将现有的年龄列和居住时长列转换为整数,因为本来是字符型,即使更新为数字,列类型仍然为字符类型,所以我们使用了astype变成了整数。

从结果可以看出,年龄和居住时长确实相关,但是其实不是最相关的,职业最为相关。当然,总体相关度都比较弱。

不过,这个数据非常大,因此我们接下来借助于可视化方法来展示下。

import seaborn as sns

sns.heatmap(frame.corr())

plt.show()

这里我们使用一个seaborn库,该库依然还需先外部导入项目,同时它不是独立使用的库,要结合matplotlib库。用法很简单,先在代码中导入库,调用heatmap生成热力图,这是最适合显示数据相关度的可视化图。

从中可以看出,从整体来看,上述类别之间都存在较高的相关性,已婚状态和年龄呈现明显相关性,当然这是常识。但是年龄列确实与很多其他列都存在较为明显的相关性。而且,我们还注意到产品类别1和产品类别2和购物额反而呈现一定的负相关,也就是购买了这两类商品的用户往往购物额更低。

seaborn提供的功能非常强大,使用却很简单,我们再看看分层相关性热力图。

sns.clustermap(frame.corr())

plt.show()

这里,它不仅进行了相关度热力图的绘制,而且还给出了一种特征的聚类,看的出来,不同的属性特征可以根据不同的相似程度归入到一个类别下。

seaborn甚至还能提供一些更为基础的分析功能:

sns.countplot(frame[‘Age’])

plt.show()

比如可以显示属性特征各个数值及其个数统计,使用方法更为简单,无需我们主动分组。

效果类似于柱状图,大家可以多了解。不过,如果数据比较大,部分练习可能速度较慢,大家需要安心等待。

Python大数据分析5:立体图绘制

本节主要介绍一些基本的三维图形绘制方法。三维图形表现为立体形状,一般至少三组数据才能实现图形元素的表现,相对来讲,显示复杂度较高。

我们先来搭一个基本框架。

import matplotlib.pyplot as plt

from mpl_toolkits.mplot3d import Axes3D

plt.subplot(projection=’3d’)

plt.show()

这里的subplot函数就是设置当前绘图环境为三维,同时需要导入必要的Axes3D类型。

这个就是三维绘图坐标空间,右下角会及时显示当前鼠标位于的空间坐标,

拖拽还能实现自由三维旋转。

我们先从线性图开始,绘制由线段组成的三维折线。为了能介绍清楚,我们先从一个简单例子说明起:

x = (0, 0, 1, 4, 5)

y = (1, 1, 1, 1, 2)

z = (2, 0, 3, 4, 5)

plt.plot(x, y, z)

plt.show()

这些折线的关键在于点,因此需要我们给出这些点的三维坐标,我们可以定义三个轴的坐标值,分别保存为三个列表,然后设置到plot中即可。

从图中可以看到,每个列表都是一个维度的数据,列表的长度就是点的个数,相应的线的个数应该是点的个数减一。

我们仍然以这一章一直在使用的股票数据为例。这次我们首先按照年、月分组,并统计

import pandas as pd

frame = pd.read_csv('C:\\temp\\股票数据.csv', encoding='GBK')
frame = frame.set_index('日期')
frame.index = pd.to_datetime(frame.index)
results = frame[['收盘价']].groupby([frame.index.year, frame.index.month]).mean()
print(results.index.codes[0])
print(results.index.codes[1])
print(results['收盘价'])

这个分组过程并不复杂,关键是如何获取分组后两个分组条件列,这里我们使用了codes属性,为0的就是第一个分组条件,即年,以此类推,为1的就是第二个分组条件,即月。此时,分组数据本身的大小意义不是很大,因此此时都会自动换成了数字序列。

然后我们就以这三个数据列来表达三维信息,

plt.subplot(projection=’3d’)

plt.plot(results.index.codes[0], results.index.codes[1], results[‘收盘价’])

plt.show()

当然,目前的结果似乎没有很好的展示效果,

通过旋转调整,可以看出随着年度的不断推移,月份不断从1到12再到1,就会产生明显的折线,并且平均收盘价不断呈现下降趋势。

而且,在早期一年当中的大起大落想象非常明显,后期一年当中的起伏相对变低。这也体现了这种三维分析的特点,往往可以进行更多维度的多角度观察。

接下来我们看看三维散点图。一般而言,三维线性图意义往往不大,除非结合时序关系,否则点与点之间的连线无法说明实质的意义。相对而言,散点图就更为常见,只需绘制点即可。

首先我们先取得收盘价、换手率、成交笔数三个数据按照年月汇总的平均值:

results = frame.groupby([frame.index.year, frame.index.month]).agg(val1=(‘收盘价’, ‘mean’), val2=(‘换手率’, ‘mean’), val3=(‘成交笔数’, ‘mean’))

print(results)

然后还是调用scatter函数实现:

ax = plt.subplot(projection=’3d’)

ax.scatter(results[‘val1’], results[‘val2’], results[‘val3’])

plt.show()

请注意,这里不能使用plt的scatter函数,因为它是二维散点图,如果要三维,需要理由plt的subplot函数返回当前绘图环境后再调用scatter,这个可以支持。

在图中旋转图形可以看出,大部分换手率都集中于2前后,

而且随着收盘价的不断增长,更易于出现成交笔数变大的趋势。

柱状图也可以进行三维绘制。比如可以按照年份,将每年的各个月统计数据分别做成一张柱状图,多次依次绘制后,就会形成三维效果:

results = frame[[‘收盘价’]].groupby([frame.index.year, frame.index.month]).mean()

plt.bar(results.index.codes[1], results[‘收盘价’], zs=results.index.codes[0])

plt.show()

其中,前两个分别表示在其中一个二维平面上传统的柱状图,分别是横轴月份,纵轴收盘价,最后一个zs参数表示第三个维度,即年份。

效果虽然看的出来,但是颜色并不能很好的区分。

为此,可以根据月份或者年份、甚至收盘价来设置不同的颜色:

import matplotlib.cm as cm

plt.bar(results.index.codes[1], results[‘收盘价’], zs=results.index.codes[0],

        color=cm.ScalarMappable().to_rgba(results.index.codes[0]))

plt.show()

这里使用年度作为区分颜色标准,

从中可以看出各个年份的不同数据情况,仍然不同年份升降趋势并不相同,但是总体随着年份而收盘价总体逐渐下降。

与此类似的还有一个3D版本的柱状图:

ax = plt.subplot(projection=’3d’)

ax.bar3d(x=results.index.codes[1], y=results.index.codes[0], z=0, dx=1, dy=1, dz=results[‘收盘价’], color=cm.ScalarMappable().to_rgba(results.index.codes[0]))

plt.show()

它是subplot函数返回类型的方法,参数设置略微复杂些,其中可以x、y、z分别是每个方柱的底部点的坐标,为了底部对齐,我们把z设置为0。后面的dx和dy分别表示每个方块的宽度和厚度,而高度则可以定义为收盘价等统计数据。

效果差不多,但是立体感更强。

Python大数据分析4:饼状图绘制

前面所介绍的一些图例更多的是展示数据本身数值的大小,而饼状图不一样,更侧重于展示数据的相对大小。

首先我们得到每年该股票的成交笔数总和,然后直接进行饼状图绘制:

results = frame[[‘成交笔数’]].groupby(frame.index.year).sum()

plt.pie(results[‘成交笔数’])

plt.show()

从中可以明显看出不同年份占据的比例,也就是相对大小,具体数值的绝对值大小往往不能有效的直观反映。

为了将数据展示的更为清楚,我们可以加上必要的数据信息:

plt.pie(results[‘成交笔数’], labels=results.index, autopct=’%3.1f%%’)

plt.show()

这里的labels用于显示每个饼块的标签,autopct可以设置每个饼块的占比数值及其格式,这里3表示三位整数,1表示一位小数,后面再加一个百分号,前后的百分号用于标记。

大家要注意,第一个年份2011是从0度方向开始,直至逆时针环绕一圈。

此时也可以调整下字体,以更适合显示效果:

plt.pie(results[‘成交笔数’], labels=results.index, autopct=’%3.1f%%’,

        textprops={‘color’: ‘b’, ‘size’: 15, ‘weight’: ‘bold’})

plt.show()

其中的textprops中使用字典分别设置了颜色、大小和黑体。

当然具体效果要综合实际搭配才能看的出来。

和前面图形一样,饼状图也提供了显示多种数据的途径。比如我们可以利用饼块与中心的分离距离来表示其他数据:

results = frame.groupby(frame.index.year).agg(sum=(‘成交笔数’, ‘sum’), avg=(‘换手率’, ‘mean’))

plt.pie(results[‘sum’], explode=results[‘avg’]/results[‘avg’].max(), labels=results.index, autopct=’%3.1f%%’)

plt.show()

这里我们也获取了换手率的年度平均值,按照与它最大值之比,设置为饼块与中心的分离距离。

从结果可以看到,离中心越远的,换手率越大,比如2011年和2015年的最大,也最远。explode效果非常不错,特别用于突出显示其中明显的数据块。

也可以使用柱状图中利用颜色表示数据大小的方式:

plt.pie(results[‘sum’], colors=cm.ScalarMappable().to_rgba(results[‘avg’]), labels=results.index, autopct=’%3.1f%%’)

sm = plt.cm.ScalarMappable(norm=plt.Normalize(vmin=results[‘avg’].min(), vmax=results[‘avg’].max()))

plt.colorbar(sm)

plt.show()

这里仍然能够看出2011年和2015年换手率最大。

饼状图还可以使用一种较为有效的重叠显示方式来展示更多数据。我们可以首先可以先将现有饼图收缩下:

plt.pie(results[‘sum’], wedgeprops={‘width’: 0.2, ‘edgecolor’: ‘w’}, labels=results.index, autopct=’%3.1f%%’)

plt.show()

其中,wedgeprops属性中的width宽度就是说明将现有饼块的半径保留到0.2。

从图中可以看出,从外面向圆心保留20%,成为一个扇形,边为白色。

但是原有的文字标签还是居中对齐,位于整个圆心半径的中间。

为此可以再次调整标签的位置:

plt.pie(results[‘sum’], pctdistance=0.9, wedgeprops={‘width’: 0.2, ‘edgecolor’: ‘w’}, labels=results.index, autopct=’%3.1f%%’)

plt.show()

大家可以想一想,0.9这个单位正好位于20%的中间。

图中看的很清楚,其实大家可以自己多尝试来调节出更多的实际效果。

我们在里面再增加一个扇形:

plt.pie(results[‘avg’], pctdistance=0.5, radius=0.8, autopct=’%3.1f%%’)

plt.show()

这里的关键是设置饼块的半径,0.8表示80%,正好嵌入在刚才圆形中间的空白处。pctdistance为0.5表示标签居中显示。

也可以将里面的扇形也通过缩小的方式来设置:

plt.pie(results[‘avg’], pctdistance=0.85, wedgeprops={‘width’: 0.2, ‘edgecolor’: ‘w’}, radius=0.8, autopct=’%3.1f%%’)

plt.show()

这里的方法不变,大家能看出设置的技巧吗?可以自己大胆调节尝试下。

Python大数据分析3:柱状图绘制

这里我们再介绍一些常用的图样,它们可以表达不同场景下的数据效果。

我们先看看柱状图:

# coding:utf-8
import pandas as pd
import matplotlib.pyplot as plt

frame = pd.read_csv('C:\\temp\\股票数据.csv', encoding='GBK')
frame = frame.set_index('日期')
frame.index = pd.to_datetime(frame.index)
plt.bar(frame.index, frame['收盘价'])
plt.show()

这里关键就是一个bar函数,它至少有两个参数,一个是横轴数据,一个是纵轴数据。

从图形中看的出来,它就是一条一条垂直线段,每个线段的长度就是收盘价。显然,这种方式其实更适合使用线性图或者散点图来做,通常柱状图更适合表现一些数据量相对较小的数据。

我们先对数据按照年份进行分组:

results = frame[[‘收盘价’]].groupby(frame.index.year).mean()

print(results)

这里显示的是每个年代收盘价的平均值。

我们对其进行柱状图显示:

results = frame[[‘收盘价’]].groupby(frame.index.year).mean()

plt.bar(results.index, results[‘收盘价’])

plt.show()

我们对分组后的数据,将年份索引设置为横轴,平均收盘价作为纵轴。

结果看的很清楚。不过,好像柱子有点太宽了,颜色也不好看。

我们对样式调整下:

plt.bar(results.index, results[‘收盘价’], 0.2, label=”close”, color=”#87CEFA”)

plt.legend()

plt.show()

这里很多大家应该都知道是什么意思,0.2表示宽度,变成标准宽度20%,label需要legend才能最终显示出来,这里的color我们使用了一种新的表示方法,称之为红绿蓝三色系,每种颜色占据两个字符,分别是十六进制的一个数字,各自代表红绿蓝各为什么颜色,形成一种组合颜色,显然这样颜色定义更为丰富多彩。

一般而言,这种图形都是通过方柱的长短表达所需显示数据的大小,如果想同时展示更多的数据内容,那就可以需要通过设置其他参数来表达。正如散点图可以同时通过点坐标、点大小、点颜色等方式来表达数据一样。

比如我们准备通过方柱的宽度来表达下换手率。首先我们先得到按年份汇总的收盘价和换手率:

results = frame.groupby(frame.index.year).agg(avg1=(‘收盘价’, ‘mean’), avg2=(‘换手率’, ‘mean’))

print(results)

这里我们使用agg同时统计了两个不同列的年度平均值。

然后使用年度平均收盘价作为标准方柱长度数据,换手率作为方柱宽度,由于宽度表示比例,一般最好为0到1之间,因此我们故意除以10,以将所有宽度变小。

plt.bar(results.index, results[‘avg1’], width=results[‘avg2′] / 10, color=’#87CEFA’)

plt.show()

从图中相对大小依然能看出换手率的大小。

通过颜色来区分其他数据的大小也是可行的,

import matplotlib.cm as cm

plt.bar(results.index, results[‘avg1’], color=cm.ScalarMappable().to_rgba(results[‘avg2’]))

plt.show()

不过要注意,这里不能直接利用数据来表示颜色,需要调用matplotlib库中子类型的to_rgba函数转换为颜色序列。

一般默认情况下颜色越浅数据越高。

如果想看出每种颜色对应的数值:

sm = plt.cm.ScalarMappable(norm=plt.Normalize(vmin=results[‘avg2’].min(), vmax=results[‘avg2’].max()))

plt.colorbar(sm)

plt.show()

那就需要加上colorbar颜色条,但是此时需要指定颜色条的数值范围,这些内容略复杂,不过大家可以自行根据需要修改使用即可,看得出,只需设置下ScalarMappable参数中那个最小值和最大值即可。

结果更为清楚了。

也可以通过显示多个方柱来展示多个数据,但是考虑到数据范围,最好是具有相似数据范围的数据才好进行展示。比如我们展示年度平均最高价和最低价,这里首先需要得到年度平均最高价和最低价,这个不难,接下来,需要绘制两个方柱。

可以调用两次bar函数,但是位置略有差异:

results = frame.groupby(frame.index.year).agg(high=(‘最高价’, ‘mean’), low=(‘最低价’, ‘mean’))

plt.bar(results.index, results[‘high’], width=0.2, color=”r”)

plt.bar(results.index + 0.2, results[‘low’], width=0.2, color=”b”)

plt.show()

第二个bar显示的位置也加了0.2,和第一个方柱宽度一样,可以想象它会向右移动。

细心的同学可以看出横轴的点不再居中对齐,仍然还是对齐第一个bar的中间。

为此,我们还可以继续调整x轴显示的内容位置:

plt.xticks(results.index + 0.1)

plt.show()

这个0.1是半个方柱的宽度。

正好对齐了现在两个相邻方柱的中间。从中看出有的年份两者相差不大,而有些年份差异很大。

为了更突出不同线段的差异,我们还可以设置y纵轴的显示范围:

plt.ylim(5, 35)

plt.show()

此时可以根据需要有选择的显示部分图的内容,增强显示数据的效率。

也可以考虑将不同的方柱绘制在同一位置上产生叠加效果:

plt.bar(results.index, results[‘high’], width=0.2, color=”r”, alpha=0.5)

plt.bar(results.index, results[‘low’], width=0.2, color=”b”, alpha=0.5)

plt.show()

这里两个方柱都是同样的横轴,因此后来绘制的方柱会在前面方柱上面继续绘制。为了防止后绘图形完全盖住了前面的图形,可以考虑将透明度增加下:

我们可以看到此时的效果。

也可以不重叠的叠加,直接将第二个方柱绘制在第一个方柱的正上方:

plt.bar(results.index, results[‘high’], width=0.2, color=”r”)

plt.bar(results.index, results[‘low’], bottom=results[‘high’], width=0.2, color=”b”)

plt.show()

此时需要利用bottom属性指定起始纵轴位置。

看的出来,这样就不用担心互相遮挡的问题了。

最后我们来看看两个更为奇特的效果。比如一个横向的柱状图,barh函数表示水平方向的柱状图:

plt.barh(results.index, results[‘收盘价’])

plt.show()

用法类似,只是横向显示。

甚至可以改变平面坐标,换为极坐标,就产生出另外一种效果:

results = frame[[‘收盘价’]].groupby(frame.index.year).mean()

plt.subplot(projection=’polar’)

plt.bar(results.index, results[‘收盘价’])

plt.show()

这里的subplot表示中参数的设定为极坐标。

此时的效果更为炫目些。

Python大数据分析2:图形样式的美化

图形最初显示的样式往往并非十分好看,我们可以通过大量的设置为完善图形的显示。事实上,matplotlib样式设置功能非常强大,因此在这里我们也主要结合一些最为常见的功能来做介绍。

比如我们想改变线性图的样式:

plt.plot(frame[‘收盘价’], label=”close”, color=”red”, linewidth=2)

plt.legend()

plt.show()

这里的设置都比较见名知意,分别是标签、线条颜色和宽度,2表示两个像素,越大越粗。注意,一定要在show显示前加上legend方法,否则无法看到标签文本内容。

大家可能注意到了红色的曲线,文本标签默认位于右上角。这个标签通常在绘制多条线条时可以很好的区分哪条线条是哪一个数据。

如果要想改变,还是需要修改legend:

plt.legend(loc=9)

此时为上部居中显示。具体数字含义可以参考网上资料,或者自己也可以尝试观察下。

这里有一个常见问题,如果写成中文汉字标签,就会无法正确显示。个中原因主要是无法找到对应的中文字体。对于此类问题,操作略微麻烦些,不过只需复制过去即可:

# coding:utf-8
import pandas as pd
import matplotlib.pyplot as plt
from pylab import mpl

frame = pd.read_csv('C:\\temp\\股票数据.csv', encoding='GBK')
frame = frame.set_index('日期')
frame.index = pd.to_datetime(frame.index)
plt.plot(frame['收盘价'], label="收盘价", color="red", linewidth=2)
mpl.rcParams['font.sans-serif'] = ['SimHei']
plt.legend()
plt.show()

其中首先导入pylab库的mpl类型,然后通过它的rcParams函数设置中文字体,其中SimHei就是我们常见的黑体。

可以看出正确的显示了所需的中文及其正确的字体。

我们继续增加些常见的文字说明,

plt.xlabel(“Year”)

plt.ylabel(“Price”)

plt.title(“Close Price”)

这里分别是横轴、纵轴和顶部标题的各自设置方法。

从图中来看,都比较直观和增加图形的可读性。

同样,如果是中文汉字,也需要设置字体,并在输出汉字时使用该字体:

from matplotlib.font_manager import FontProperties

font = FontProperties(fname=”c:\\windows\\fonts\\simsun.ttc”, size=14)

plt.xlabel(“年份”, fontproperties=font)

plt.ylabel(“股价”, fontproperties=font)

plt.title(“收盘价”, fontproperties=font)

其中,fname需要指定字体所在的位置,这里是宋体文件名称,也是默认的位置。

常见的文字信息一般就是这些,下面我们来看看图形的设置。

一般而言,默认的线条样式还是过于简单,我们来改一下:

plt.plot(frame[‘收盘价’], “.”)

plt.show()

这种改动位于数据列后面的一个参数,表示点的样式。

可以看出这里不再使用直线连接各个点,而是只绘制当前点。

点可以改变,甚至可以变成其他的模样。

plt.plot(frame[‘收盘价’], “+”)

这里表示加号点。

大家可以自行到网上去查找下更多的设置方法。

这个参数同时还可以设置颜色和点之间连线的类型:

plt.plot(frame[‘收盘价’], “+r:”)

plt.show()

这里是红色的虚线。

进一步结合更多属性设置,可以设置出更为复杂的效果:

plt.plot(frame[‘收盘价’], “.r”, markersize=30, markeredgecolor=’orange’, markeredgewidth=2)

这里的点的大小、边缘的颜色、和边的宽度都可以设定。

效果和美观当然需要大家根据数据和自己的理解做出组合和应用。

当然,不同的图形,样式设置也有一些差异,比如我们如果使用散点图来绘制这个图形,就可以单独来设置每个点的样式:

plt.scatter(frame[‘开盘价’], frame[‘收盘价’], c=’r’, s=10, alpha=0.6)

这里的c表示点的颜色,s表示点的大小,alpha为透明度,在点与点重叠时,增加点透明度可以更好的看清点点之间的重叠,1为默认的不透明。

这些都可以从结果看的出来。

进一步完善下:

plt.scatter(frame[‘开盘价’], frame[‘收盘价’], c=frame[‘成交量’], s=frame[‘换手率’], alpha=0.5)

plt.colorbar()

plt.show()

这里展示了散点图一种优势,即不仅可以通过二维坐标同时展示两组数据的对应关系,还能通过点的颜色和大小表达更多的数据内容。比如这里我们使用成交量分别映射成不同的颜色,为了能够看清建议此时加上colorbar。换手率通过点的大小来表示。

从图中可以看出,成交量最大的是黄色,颜色对应的数据为三千八百万左右,对应的开盘价和收盘价都在20左右,

这个和数据实际情况是一致的。

图中的最大的点表示最大的换手率,对应的开盘价和收盘价都在40左右

这个和数据实际情况是一致的。

除此以外,还可以直接利用系统定义好的一些样式主题,如:

plt.style.use(‘dark_background’)

plt.scatter(frame[‘开盘价’], frame[‘收盘价’])

plt.show()

这个黑色背景显得很深邃些。

要想知道系统有哪些可用的样式,可以使用下面的语句来查看:

绘图可以同时绘制多个图形,比如我们绘制两个不同的股票信息:

# coding:utf-8
import pandas as pd
import matplotlib.pyplot as plt

frame1 = pd.read_csv('C:\\temp\\股票数据.csv', encoding='GBK')
frame1 = frame1.set_index('日期')
frame1.index = pd.to_datetime(frame1.index)

frame2 = pd.read_csv('C:\\temp\\600339.xls')
frame2 = frame2.set_index('date')
frame2.index = pd.to_datetime(frame2.index)

plt.plot(frame1['最低价'])
plt.plot(frame2['close'])
plt.show()

一般绘图会自动采用不同的颜色在区分表示。这里由于数据时间起至不一样,因此两个线条长短不一。

为此,我们可以调整下两个轴显示数据的范围:

plt.xlim(datetime(2012, 1, 1), datetime(2019, 12, 31))

plt.ylim(0, 100)

记得导入datetime。

显示内容确实调整了x和y轴数据的范围。

当然,有时也可以分开显示,这可以以子图的方式来实现:

plt.subplot(2, 1, 1)

plt.plot(frame1[‘最低价’])

plt.subplot(2, 1, 2)

plt.plot(frame2[‘close’])

plt.show()

这里的subplot就是子图的意思,2表示纵向两个,1表示横向一个,

最后一个1和2表示对应哪一个。显然1在上面,2在下面。

Python大数据分析1:数据可视化基本方法

数据可视化本身不仅是一种数据结果的呈现方法,而且也提供了一种分析数据的角度,尤其是一些结合大数据和时空信息的可视化展示,往往能够给我们提供一些单独从数据内容上无法直接和直观获取的认识和发现。因此数据可视化几乎是所有数据分析工具必备的功能。在前文章节中,我们简单介绍过基本使用,从这一章开始,我们开始深入了解一下。

我们还是回到从以前的问题开始,即股票数据那个例子。

# coding:utf-8
import pandas as pd
frame = pd.read_csv('C:\\temp\\股票数据.csv', encoding='GBK')
frame = frame.set_index('日期')
frame.index = pd.to_datetime(frame.index)
print(frame)

这里将数据文件直接加载成DataFrame,并且将日期设置为索引,并转换为Datetime时间类型,以方便处理。

需要补充说明下,因为代码和显示内容中含有中文,因此如果出现字符编码错误,

请核实是否将当前编辑器的文件编码设置为默认的GBK。

没有问题的话,我们就首先还是使用最为简单的线性图来绘制。所谓线性图,就是一般最为常见的二维坐标图。我们需要设置的只需x和y两个轴的数据,这些xy数据两两对应,会在屏幕上生成很多点,连续两点之间使用直线连接。如果数据较为稠密,远远看去,这些由很多很短的直线连接起来的完整线会看起来呈现圆滑曲线的效果。

这是最为简单基本的代码:

# coding:utf-8
import pandas as pd
import matplotlib.pyplot as plt

frame = pd.read_csv('C:\\temp\\股票数据.csv', encoding='GBK')
frame = frame.set_index('日期')
frame.index = pd.to_datetime(frame.index)
plt.plot(frame['收盘价'])
plt.show()

其中导入了matplotlib库中的pyplot类型。matplotlib是一个非常常见好用的Python绘图库,它的plot函数就是线性图的意思,show表示显示。这些都是最为基本的使用。

这里大家可能会问,为什么plot只有一个数据?那是因为默认情况下,索引就是一个数据列,而且是x横轴列,所以这也是为什么一开始我们就设置DataFrame索引的原因。

如果我们想改变,那就需要自己写出两个数据列。

plt.plot(frame[‘开盘价’], frame[‘收盘价’])

plt.show()

这里绘图时就不再采取默认的索引作为横轴,而是第一个列为横轴,即开盘价,第二个列为纵轴,即收盘价。

图像看起来有点怪,主要原因在于这里每个时间相邻的两个点都画上了直线,显然这是多余的。但是,我们发现大部分点都位于对角线上,说明开盘价和收盘价基本上差不多,因为股市一天涨跌有10%的限制,开盘价高的点靠右下方,收盘价高的靠近左上方。

如何解决这个比较怪异的显示呢?其实这时我们无需绘制连续两点之间的直线,只需将点绘制出来即可,这个可以通过改变线性图的显示效果来实现:

plt.plot(frame[‘开盘价’], frame[‘收盘价’], ‘ro’)

plt.show()

这里的第三个参数表示点的样式,r表示红色,o表示显示一个点。

此时的结果就变成了红色的点状图。

因此线性图就显得不太合适,而应该使用散点图。

plt.scatter(frame[‘开盘价’], frame[‘收盘价’])

plt.show()

从中可以看到此时的点点之间不再具有连接直线。这张图很好的说明了在有些价格组合区间更为稠密,比如在15元左右,说明这段时间出现的行情较多,而有些则更为稀疏,比如25元左右。

这也说明,我们在选择图形种类时要能根据需要有选择的使用,而不是一味的随意使用,同时也需要了解一些常见图形的种类和特点。

最后我们来谈谈绘图的一些整体性设置问题。这些步骤可以更好的帮助你完成一些设置,比如图形的大小。默认情况下,大家可能感觉到了,每次生成的图形都不是很大,如果想设置为更大的尺寸,可以设置为:

plt.figure(figsize=(9, 9), dpi=100)

该语句只需在show显示前设置即可,9表示英寸,大约每英寸有72个像素点,显然越大的话,图形就越大,后面这个dpi表示分辨率,可以理解为笔头的粗细,显然越小图像越清晰,越大粒度越大越粗糙。

另外,有时为了分享方便,我们需要将图片保存下来,这个即可以在图形显示界面中点击最右边的保存按钮来进行,

也可以通过代码直接保存而无需显示。

plt.savefig(“D:\\stocks.png”)

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’])

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

Python大数据分析7:案例-让股票趋势画出来

我们接着这个股票的案例再来说明。

在实际数据分析中,我们常常需要对时序数据进行可视化,通常可视化呈现,可以更好的把握数据的整体走向分析和发展特点分析。

比如对于目前这个近10年的股票数据,我们很想看看整体走势是什么样子?于是我们准备利用Python的可视化绘图工具包来实现。

Python有很多可视化绘图工具包,其中比较常见的是matplotlib。我们来看看。

要想使用这个工具包,首先还是要像以前使用pandas一样,在设置中安装,具体仍然是点击文件菜单,选择设置,在项目选中项目解释器,并点击右边的加号,在弹出窗口中输入matplotlib,即可搜索安装。具体过程不再细述。

首先准备好数据,我们按照上次案例中的写法,将时间信息抽取为索引,做好进一步数据分析的准备工作。

# coding:utf-8

import pandas as pd

frame = pd.read_csv(‘C:\\temp\\股票数据.csv’, encoding=’GBK’)

frame = frame.set_index(‘日期’)

frame.index = pd.to_datetime(frame.index)

print(frame)

接下来,我们从简单的开始,直接看看每日收盘价的变化。

# coding:utf-8

import pandas as pd

import matplotlib.pyplot as plt

frame = pd.read_csv(‘C:\\temp\\股票数据.csv’, encoding=’GBK’)

frame = frame.set_index(‘日期’)

frame.index = pd.to_datetime(frame.index)

plt.figure(figsize=(8, 4), dpi=100)

plt.plot(frame[‘收盘价’])

plt.show()

这里的关键点有几个:

第一要记得导入matplotlib包,为简化写法,可以给包起个别名。

import matplotlib.pyplot as plt

第二需要设定下画布的大小和分辨率,8和4分别表示默认的英寸,dpi表示分辨率,越小图像越清晰,越大粒度越大越粗糙。

plt.figure(figsize=(8, 4), dpi=100)

第三需要设定绘制的内容,此时我们直接将收盘价作为绘制的数据内容。

plt.plot(frame[‘收盘价’])

第四直接显示即可。

plt.show()

这是它的显示情况,总体呈现一种下降的趋势。不过,早期有几段间断,来自于当时没有交易产生的暂停现象。由此也看出原始数据通常需要进行一些必要的处理才能更好的看出趋势和特点。

值得注意的是,原始数据时间顺序是从最近到最远,即使再混乱也没有关系,最终时间索引会自动排成从远到近的时间序列。

对于这种不平整,常见的一种处理策略就是平滑,比如使用移动窗口方法,将几个临近时间点上的数据进行平均等操作,产生一种更为平滑的显示效果,同时还不改变整体显示趋势。

比如我们进行了包括每个点在内往前200个数值,不断滚动处理,对于每个时间点对应的200个数据都以一个平均值作为该时间点的最终输出数值。

plt.plot(frame[‘收盘价’].rolling(200).mean())

大家注意线条开始的位置偏后了很多,共计200天的数据量。

但是可以想象,这个图的一开始,由于每个时间点前没有200个数值,因此无法完成滚动计算,一般系统会使用空值来填充。为了能够使得图像更为完整,可以考虑对于前面200个时间点,前面要求的时间点数量也可以适当缩小,比如通过min_periods参数指定最小的数量范围,从而使得空值产生的更少一些。

print(frame[‘收盘价’].rolling(200, min_periods=2).mean())

显然这个有值观测点的最小数量应该小于200,大于等于1,当等于1时,表示每个观测点都有值。

这些可以通过数据输出看的更为明显一些。

当然,为了实现更好的分析,还可以尝试使用更多的方法。比如扩展窗口平均,它是指从时间序列的起始处开始窗口,不断增加窗口长度直到它超过所有的序列,形成长度越来越长的各个序列。这里的10是指min_periods,即有值观测点的最小数量。

plt.plot(frame[‘收盘价’].expanding(10).mean())

不同的分析方法具有不同的价值,大家可以通过学习一些必要的统计时序分析方法来加深对方法应用的理解。

再如指数加权移动统计,它可以实现时间衰减效果,各数值的加权系数随时间呈指数式递减,越靠近当前时刻的数值加权系数就越大。

plt.plot(frame[‘收盘价’].ewm(span=30).mean())

span表示指定衰减的范围。

再比如百分数变化,即后一个值和前一个值的差值占前一个值的比例。

plt.plot(frame[‘收盘价’].pct_change())

其中几次较大的幅度正好对应中间几段空白的停止交易记录。

比较复杂的还可以计算相关性,比如对开盘价和收盘价,进行两者平滑后的相关度:

series1 = frame[‘开盘价’].pct_change()

series2 = frame[‘收盘价’].pct_change()

plt.plot(series1.rolling(20).corr(series2))

corr表示相关度计算。

显然,大家如果想要更好的使用这些时序方法,首先就要了解这些常见统计方法,其次也要知道pandas提供了哪些对应的实现方法,这样就可以更好的应用。

如果已有的这些方法都不能满足要求,那就需要自己来编写函数扩展功能。这里我们并不打算介绍太深入,但是可以把基本方法说明下,那就是仍然调用apply方法实现对更多自定义函数的调用。这一块介绍主要面向有Python基础的同学。

比如我们定义了一个求平方的函数:

def f(x):

    return x * x

然后我们对每个平滑后的平均值做进一步的平方处理:

plt.plot(frame[‘收盘价’].rolling(10).mean().apply(f))

可以看出,纵轴数据明显变大了。

Python大数据分析6:案例-每天股票涨了多少?

时间序列不仅可以用于前面所说的数据记录中的列表示和索引表示,也广泛应用于各种时间计算等常见操纵中。这次我们准备结合一些时间分析的例子,来看看各种时间序列的操作方法。

为了能够讲清楚这些问题,我们需要一个例子,这里以一家公司的股票信息来做分析。

首先我们需要解决如何读取这个数据文件,在pandas中,读取文件很简单,可以利用pandas的read_csv函数即可,比如:

frame= pd.read_csv(‘macrodata.csv’)

这就表示读取macrodata.csv文件,将其中的所有数据直接生成DataFrame变量,一般CSV文件就是以列表示各种字段、每行一个记录的数据块,可以直接保存到DataFrame变量中。

请注意,当前这个数据文件是放在项目中,因此可以直接读取。

但是在实际操作中,数据文件往往位于其他地方,而且可能含有汉字信息。比如我们这个股票数据文件是“股票数据.csv”,位于C盘temp目录下。直接使用上述方法:

frame= pd.read_csv(‘C:\\temp\\股票数据.csv’)

说明下,这里分隔C盘和目录的反斜杠应该写成两个。了解Python的同学应该知道这是转义字符,不太清楚的同学也应该知道这就表示分隔符。

不过此时会出现错误,主要原因在于这个文件含有汉字信息。

正确的处理方式应该是增加解析汉字所需的字符集,即使用encoding参数:

frame= pd.read_csv(‘C:\\temp\\股票数据.csv’, encoding=’GBK’)

一般而言,大家可以尝试使用诸如“GBK”、“UTF-8”、“GB2312″等字符集中的一个,由于大家使用的电脑环境都各不一样,很难说哪一个有效,不过,一般而言,上述三个中的一个总可以满足要求。

如果我们直接输出DataFrame内容,由于数据很大,所以导致我们无法看到所有数据,这里面有两种方法可以供大家参考:

第一是无需读取全部记录,避免加载数据很多导致的速度过慢,方法是只读取部分记录:

print(frame.head(5))

这里的head就表示取得前面若干行记录。

第二是即使数据很少,但是如果列很多的话,也会有省略的显示内容,因此可以设置下默认的显示方式,即通过pandas的set_option函数设置下一些常见的显示参数,比如:

pd.set_option(‘display.max_columns’, None)

就表示将全部的列都显示出来。

但是,由于横轴宽度太窄,因此需要再次加宽下整体显示宽度:

pd.set_option(‘display.width’, 500)

其实在很多情况下,我们都可以借助这两个参数设定来调整显示。

最后一步常见的操作就是将时间信息设置为当前数据的索引。方法很简单,通过DataFrame的set_index方法即可实现:

frame = frame.set_index(‘日期’)

frame.index = pd.to_datetime(frame.index)

在结果中我们可以看到,一旦将一列设置为索引,则此列就自动从正式列中被除去。同时,为了时间处理方便,我们再次将索引转换为时间类型。

好了,到此为止,我们完成了一些时间分析中最为常见的基本数据准备工作。

接下来我们想结合一些基本操作方法来看看它们的实际应用价值。

比如我们想看看每天比前一天变化多少,既可能增长也可能下降,只需把当天的收盘价减去前一天的收盘价即可计算。为此我们需要按照日期来对齐两种数据:

这里我们使用了shift方法,它表示移动,我们可以先观察下:

print(frame.head(5).shift(1))

从结果看的出来,第一行全部移到了第二行,第二行再全部移动了第三行,以此类推,同时,第一行全部被空值填充。

由于此时索引并没有变化,因此我们可以按照索引对全部数据对齐,进行直接计算:

print(frame[‘收盘价’] – frame[‘收盘价’].shift(1))

其中,虽然是按照每天时间对齐数据,但是其实计算的是当天数据和前一天数据的差值,正好满足了我们的要求。

不过,由于记录行太多,依然无法看到全部内容,此时可以再次设置参数来调节下:

pd.set_option(‘display.max_rows’, None)

当然这个参数一般尽量不要设置,避免返回的数据过多导致显示速度太慢。

除了可以移动内容外,也可以移动索引本身,比如我们为了计算同样的内容,可以将现有索引往前移动一天,方法是:

print(frame[‘收盘价’].shift(-1, freq=’D’))

此时内容没变,但是对应的时间都往前提了一天。

因此还可以同样计算当天数据和前一天数据的差值:

细心的同学可能注意到,这个结果基本和前面是一样的,但是中间多了一些空值,主要原因在于这个时间序列并非连续序列,股票交易只有工作日的记录,周末两天没有记录,移动日期后,序列中间产生了很多无法一一对应的时间,主要就是周末前后两天的错位,从而产生一些空值,而且产生的时间和刚才移动数据会产生不同。

Python大数据分析5:时间采样

时间采样也可以看成是一种时间转换,不同之处在于它往往都采用删除数据或者填充数据的方式来对原始数据本身进行处理。

我们从一个例子开始:

print(frame[‘height’].resample(‘M’).mean())

这个就表示对当前时间进行按照月份为单位的采样,原始的天数信息被粒度更大的月份所替代,这个过程就是一种时间转换。但是单独的使用resample转换没有意义,通常我们的目的是需要对转换后做什么给出说明。比如这里是计算每个月份的身高平均值。

其中,我们还注意到由于粒度变大,因此每月都以当月最后一天来表示。

如果想只显示月份,那么可以通过kind参数来设置:

print(frame[‘height’].resample(‘M’, kind=’period’).mean())

此时period值表示按照月度周期来显示,就是月份信息本身。

细心的同学可能也会想到,这种方法借助于分组不也可以完成吗?当然可以,我们可以将时间索引转换为月份,并分组,显示身高平均值,效果也是一样:

print(frame[‘height’].groupby(frame.index.to_period(freq=’M’)).mean())

但是,采样方法远远不止这个简单,接下来的很多操作就必须利用采样来完成了。

比如我们准备将粒度变小来采样:

print(frame[‘height’].resample(‘H’).mean())

这里的H表示小时,看的出来每天都被强制分隔为24个记录,不过平均身高这个数据只显示一次,多出来的采样记录都为空值。原始时间往往只对应那个最对应的时间点。对于这些空值,显然对于后续的分析可能带来不利的影响,那么这该如何处理呢?

第一种方法最为简单,就是直接利用上级时间粒度对应的数值来填充:

print(frame[‘height’].resample(‘H’).ffill())

这里的ffill方法就实现了所有数值的填充。

可以限定填充的范围,比如只填充到对应时间前后各一个数值,共计2个:

pd.set_option(‘display.max_rows’, None)

print(frame[‘height’].resample(‘H’).ffill(limit=2))

这里需要看到全部记录,因此我们调整了显示全部记录,在中间,我们可以看到对应时间点的前后都被填充个一个数值。

这里面有一个非常关键的问题,就是时间区间的划分问题,大家都学过开区间和闭区间,因此对于这些时间点而言,如何规定它是属于哪一个时间段非常重要。比如我们看第一个记录,时间为2019年11月29日,默认为0点0分0秒。看的出来,默认是采取左闭区间,也就是说,每小时都从0分0秒开始计算,直到59分59秒为止。其实这里59秒并不准确,应该是一分钟中可以表达的带有毫秒等更细信息的最大秒数。

但是有时候,可能也需要表达成右闭区间,比如我们如果认为每小时是从0分1秒开始,其实这里的1秒也不准确,应该是一分钟中可以表达的带有毫秒等更细信息的最小秒数,同时直到下一小时0分0秒为止:

print(frame[‘height’].resample(‘H’, closed=’right’).mean())

此时的close参数表示闭区间,此时以右为准,即右闭区间。我们从结果看的出来。

大家可能没有理解这样的表达有什么实际意义,我们来看一个例子。

dates = [datetime(2019, 11, 29, 1, 2, 0), datetime(2019, 11, 29, 2, 0, 0),

         datetime(2019, 12, 17), datetime(2019, 12, 30), datetime(2020, 1, 1),

         datetime(2020, 1, 3), datetime(2020, 1, 4)]

frame = pd.DataFrame(data, index=dates)

我们把时间做了调整,其中改变了前两条,分别是2019年11月29日1点2分,和同一天的2点整。

现在我们直接按照小时采样:

print(frame[‘height’].resample(‘H’).mean())

可以发现这两个时间位于两个不同的时间段,分别计算各自的身高平均值,很好理解,默认为左闭区间,因此2点整属于一个新的时间段。

但是如果采取了右闭区间,可以看到,两个身高被平均为一个值:

print(frame[‘height’].resample(‘H’, closed=’right’).mean())

显然,在实际时间序列计算中,做出这种区分有时非常必要。

当然这个时候,就存在一个显示的问题,比如第一条记录的结果其实是从1点多开始到2点整为止,那么为何采样时间显示为1点呢?可以不可以也采取右边时间为主的表达方法呢?当然可以,我们再次设定:

print(frame[‘height’].resample(‘H’, label=’right’, closed=’right’).mean())

此时可以看出,第一条记录的时间段显示为右边的2点钟。

最后说明两个例子。

第一个结合简单的例子再次说明下。一般而言常见的时间采样往往发生在年月和季度之间,比如我们将所有的学生的时间都采样为年份:

print(frame[‘height’].resample(‘A’, kind=’period’).mean())

接下来我们使用季度来再次采样并填充:

print(frame.resample(‘Q-DEC’).ffill())

Q-DEC表示季度采样,并以十二月份作为最后一个季度的最后月份。

第二个例子是股票市场中常见的采样操作,即计算各个时间段上的四个值:第一个值(open,开盘)、最大值(high,最高)、最小值(low,最低)、最后一个值(close,收盘),合称为OHLC采样。

按照这个方法,我们对刚才按照年度采样的数据,再次进行了OHLC采样:

print(frame[‘height’].resample(‘A’, kind=’period’).ohlc())

其中,由于2019年有四个身高数值,其中第一个是1.88,也是最高值,最小值为1.78,最后一个值为1.86。大家看懂了吗?