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。大家看懂了吗?

Python大数据分析4:时间信息的转换

时间信息可以按照不同的单位进行不同形式的表示,由此就会产生各种不同层次的时间信息的转换问题。比如以月份为单位时,我们是应该从月初开始算还是从月末开始算?再如1个学年通常会从9月份到第二年的6月,等等。

我们还是以带有时间索引的学生数据为例:

# coding:utf-8
import pandas as pd
from pandas import DataFrame
from datetime import datetime

data = {'ID': ['000001', '000002', '000003', '000004', '000005', '000006', '000007'],
        'name': ['黎明', '赵怡春', '张富平', '白丽', '牛玉德', '姚华', '李南'],
        'gender': [True, False, True, False, True, False, True],
        'age': [16, 20, 18, 18, 17, 18, 16],
        'height': [1.88, 1.78, 1.81, 1.86, 1.74, 1.75, 1.76]
        }
dates = [datetime(2019, 11, 29), datetime(2019, 12, 5), 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)
print(frame)

为了后续统计的方便,我们现在想将以天数为单位的时间信息转换为以月份为单位的时间信息,可以考虑将时间信息直接转换为时间段信息,因为月份相对于天而言,是一个具有多个天的时间段。

print(frame.to_period(freq=’M’))

这里使用了DataFrame的to_period方法,就是将时间点转换为时间段,其中的freq参数指定时间段的类型。

这种转换是非常灵活的,可以根据freq参数来做出有特点的调整。比如我们认为学校的学年是从9月初开始计算,那么上述时间都应该是2020学年。此时可以定义新的freq参数值:

frame = frame.to_period(freq=’A-AUG’)

参数A表示按照年来分,AUG表示以8月作为最后一个月份,发现全部都是2020学年的年度单位了。

比较复杂的是如何将时间段转换为粒度更小的时间单位,比如我们做个调整,将所有索引时间全部保存为月份单位。

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

print(frame)

那么接下来如何将其转换为日期呢?我们可以再次调用DataFrame的to_timestamp方法,就是这个反操作:

print(frame.to_timestamp())

从结果来看,我们发现所有的日期都被重置为每月的1号。这没有办法,只能由系统给出一个默认值,此时默认为月初。

那么能不能略微修改下呢?比如重置到月底:

print(frame.to_timestamp(how=’end’))

可以使用how参数指定end即可表达这个意思。

我们也可以使用另外一个更为方便的方法asfreq,它完全通过freq参数既可以粒度变大,也可以粒度变小:

print(frame.asfreq(freq=’D’, how=’end’))

效果很类似,但是转换后的时间单位可以自由指定,D表示天,T表示小时等等,大家可以自行查阅。

比如我们还可以进行进行粒度变大:

print(frame.asfreq(freq=’A’, how=’end’))

当然,此时的how其实意义并不大,从低粒度到高粒度无需指定前后。因此省略的效果也是一样的。大家能够想的出为什么吧?

我们再看看一个例子。上述两种转换为小粒度的方法都依赖于当初生成时间段的单位,比如在前面设置9月初开始的学年练习中,如果对于得到的学年也进行小粒度时间转换,那么就会使用当时设定的时间点来表达:

frame=frame.to_period(freq=’A-AUG’)

print(frame.to_timestamp(how=’end’))

比如这里既然是8月为年的最后一月,那么转换为就是显示2020年8月底。

同样,如果设置为how为start,那么转换就是2019年9月初。

frame=frame.to_period(freq=’A-AUG’)

print(frame.to_timestamp(how=’start’))

Python大数据分析3:特殊时间序列的生成

前面所介绍的各种时间序列都是一些比较随机和杂乱的序列,在实际分析中,有时我们需要得到一种非常规律的时间序列,据此实现对特定数据的分析和统计。

我们先从最简单的时间点范围序列开始。

比如我们想得到从2020年1月1日开始的7天时间,并作为当前学生数据的索引:

pd.date_range(‘2020-1-1’, ‘2020-1-7’)

这里我们使用了pandas的date_range函数,直接生成指定时间起至的时间序列。

我们也可以将其应用于学生数据的索引。

也可以按照指定数量来生成:

pd.date_range(start=’2020-1-1′, periods=7)

其中的start指定起始时间,periods指定数量。

默认情况下,总是按照天数来计次,如果想按照其他时间单位,可以通过freq参数来设定:

pd.date_range(start=’2020-1-1′, periods=7, freq=’M’)

这个M表示月份,从结果能看出,它默认以每月月底来自动取一个时间,形成完整的时间序列。

要想月初开始,可以使用freq设置为MS:

frame = pd.DataFrame(data,pd.date_range(start=’2020-1-1′, periods=7, freq=’MS’))

可以设置为每2月取一次:

pd.date_range(start=’2020-1-1′, periods=7, freq=’2M’)

freq非常灵活,我们看看几个奇妙的设定:

比如按照1个半小时:

pd.date_range(start=’2020-1-1′, periods=7, freq=’1h30min’)

结果看的很清楚,时间类型既包括日期信息也包括小时等信息。

我们再来看看带有时间跨度的时间段范围序列。比如我们并不希望了解每个学生准确的加入时间,而是关注在哪个时间段加入,比如哪个月等。此时就需要使用一种时间段的时间类型。

比如从2020年1月起开始的7个月,可以设定起至时间范围:

pd.period_range(‘2020-01’, ‘2020-07′, freq=’M’)

结果看的很清楚,此时只以月份为单位,并不区分一个月中的天数。

正如前面一样,也可以指定数量:

pd.period_range(‘2020-01′, periods=7, freq=’M’)

这里的7表示取7个时间值。

有时我们也想得到未必是连续的时间段序列,这时可以自由指定时间段,比如通过列表:

values = [‘2019-12’, ‘2020-01’, ‘2019-11’, ‘2020-02’, ‘2019-12’, ‘2020-01’, ‘2019-07’]

然后据此生成时间段序列:

pd.PeriodIndex(values, freq=’M’)

此时当然也可以设定为索引。此处的PeriodIndex就表示时间段索引,freq很必要,表示时间段的单位。

如果采取了不同的时间段表示,就需要同时设定时间格式和freq参数:

values = [‘2019Q4’, ‘2020Q1’, ‘2019Q4’, ‘2020Q1’, ‘2019Q4’, ‘2020Q1’, ‘2019Q3’]

frame = pd.DataFrame(data, pd.PeriodIndex(values, freq=’Q’))

比如这里就表示以季度来取时间段。

灵活的借助于freq参数可以灵活的映射时间段的单位。比如我们可以把月份映射为季度:

values = [‘2019-12’, ‘2020-01’, ‘2019-11’, ‘2020-02’, ‘2019-12’, ‘2020-01’, ‘2019-07’]

frame = pd.DataFrame(data, pd.PeriodIndex(values, freq=’Q’))

一般而言,这种映射没有问题,结果也比较确定。

但是反过来,如果将季度映射为月份会怎么样?

values = [‘2019Q4’, ‘2020Q1’, ‘2019Q4’, ‘2020Q1’, ‘2019Q4’, ‘2020Q1’, ‘2019Q3’]

frame = pd.DataFrame(data, pd.PeriodIndex(values, freq=’M’))

在默认情况下,看得出来,它会自动以起始时间作为映射值,比如第四季度起始月为十月,因此显示为十月。

需要说明下,freq参数的取值不能随意写,每种功能都需要通过特定的字符来表示。如果需要了解具体情况和更多情况,大家可以自行百度了解,有很多详细的列举和说明。

在时间索引中,还有一种复合形式,就是可以单独设定年月等时间段索引值:

years = [2019, 2020, 2019, 2020, 2019, 2020, 2019]

months = [12, 1, 11, 2, 12, 1, 7]

frame = pd.DataFrame(data, pd.PeriodIndex(year=years, month=months, freq=’M’))

其中可以看到,我们在PeriodIndex创建时指定了年份和月份的时间段列表。

Python大数据分析2:时间索引

在数据分析中,时间信息不仅常常是一种被分析的对象,比如上次我们谈到的学生的生日,更多的可能往往表现在时间信息往往是一种分析的依据,比如我们按照时间统计分析各种流水记录。在这种情况下,我们常常把时间信息作为数据的索引,由此还可以利用很多专门的时间分析途径来对数据进行更多角度的时间分析。

比如我们假设目前的7条学生记录是分别在各个时间点上加入到我们的数据中来的,我们可以利用索引来表示:

# coding:utf-8
import pandas as pd
from pandas import DataFrame
from datetime import datetime

data = {'ID': ['000001', '000002', '000003', '000004', '000005', '000006', '000007'],
        'name': ['黎明', '赵怡春', '张富平', '白丽', '牛玉德', '姚华', '李南'],
        'gender': [True, False, True, False, True, False, True],
        'age': [16, 20, 18, 18, 17, 18, 16],
        'height': [1.88, 1.78, 1.81, 1.86, 1.74, 1.75, 1.76]
        }
dates = [datetime(2019, 11, 29), datetime(2019, 12, 5), 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)
print(frame)

这里要注意几个问题:

第一,为了处理时间更为方便些,我们还是使用标准的时间类型来表示时间,而不是字符串,因此需要先导入必要的datetime。

第二,我们得到一个时间类型的列表,并将其设定为当前数据的索引。

此时我们可能感觉不到,这种时间类型的索引和一般的序号索引有什么区别,其实,采用时间类型的索引具有很多传统一般索引所不具备的特殊功能,我们分别给大家介绍下:

我们先看看数据的选择。一般而言,索引最大的作用就是提供一种选择行的依据,时间类型的索引可以按照适应时间处理的方式来进行数据选择。

比如我们选择特定时间的记录,如2019年11月29日的记录:

最为完美的就是直接使用时间类型来选择:

print(frame.loc[datetime(2019, 11, 29)])

这里我们使用了loc函数,其中的时间类型索引正好对应具体的行记录。

不过更为方便的方法是我们可以直接在loc函数中使用字符串表示来定位。

print(frame.loc[‘20191129’])

这个写法相对比较灵活,即使写成不同的格式,只要常见和对应原有的表示,就可以使用。

print(frame.loc[‘2019-11-29’])

甚至还可以利用不同粒度的时间单位来选择,比如我们以月份来选择:

print(frame.loc[‘2019-12’])

这就表示选择所有19年12月的全部记录,都没问题。

也可以进行切片的范围选择,比如我们选择19年12月到2020年1月的全部记录:

print(frame.loc[‘2019-12′:’2020-1’])

注意loc函数含有下界时间。

有一个等价的函数也可以实现同样的效果,就是truncate,它表示剪除,比如选择什么时间段的内容其实等价于剪除其他时间的数据。比如还是刚才这个例子,我们只需剪除12月之前的记录即可:

print(frame.truncate(before=’2019-12-1′))

这里的before参数就说明在此之前的全部要剪除

我们再看看对时间类型索引的分组问题。一般而言,索引元素都是不一样的,因此对索引分组意义并不大,但是对于时间类型的索引,往往存在着时间相同或者不同时间粒度单位相同的可能性,也具有分组的实际意义。

假设存在着相同的时间索引值,我们可以直接对索引进行分组。这里有两种方法都可以:

第一种是在groupby参数中指定level=0参数数值,即表示对索引分组,不论是什么类型,都可以按照数值是否一样分组:

print(frame[‘ID’].groupby(level=0).count())

第二种是在groupby参数中说明按照索引分组,更为直观简单:

print(frame[‘ID’].groupby(frame.index).count())

不过这里我们需要对时间索引中的年份进行分组,因此可以继续去大胆的取索引中的年份:

print(frame[‘ID’].groupby(frame.index.year).count())

居然是可以的,还是老话,在很多情况下,大家只要经常练习,你就会发现你的很多想法就是它应该的写法。不过细心的同学可能也会说,不对啊,应该有个dt然后才是year,这里的主要原因在于时间索引类型其实并不是datetime类型,pandas自动转换为了更适合索引处理的DatetimeIndex类型,因此有些不一样

如果你真的加了dt你就会发现系统告诉你这个秘密了!

当然,既然能按照年,那一定也能按照月,甚至星期:

print(frame[‘ID’].groupby(frame.index.week).count())

显然这些对于时间分析很有价值。

Python大数据分析1:时间列的基本操作

这个章节主要介绍时间信息的处理。我们经常可以在日常生活工作中遇到各种与时间信息相关的数据,比如交易记录、访问记录等,这些信息往往按照时间形成一个个时间序列,此时,我们不仅可以通过传统的数据分析方法来对数据进行分析,而且还可以专门从时间变化的角度来分析,比如看看发展趋势、找出可能的周期、预测未来可能的数值等等。这构成了一个较为独立的分析领域。

我们先从时间的表示开始,比如我们准备给现有的学生数据增加一些时间列,比如生日:

# coding:utf-8
import pandas as pd
from pandas import DataFrame
from datetime import datetime

data = {'ID': ['000001', '000002', '000003', '000004', '000005', '000006', '000007'],
        'name': ['黎明', '赵怡春', '张富平', '白丽', '牛玉德', '姚华', '李南'],
        'gender': [True, False, True, False, True, False, True],
        'birthday': [datetime(2004, 10, 1), datetime(2000, 11, 27), datetime(2002, 1, 29),
                     datetime(2002, 8, 27), datetime(2003, 3, 14), datetime(2002, 12, 17),
                     datetime(2004, 4, 12)],
        'age': [16, 20, 18, 18, 17, 18, 16],
        'height': [1.88, 1.78, 1.81, 1.86, 1.74, 1.75, 1.76]
        }
frame = pd.DataFrame(data)
print(frame['birthday'])

这里我们首先使用了Python中专门的时间类型,首先我们通过import语句导入了datetime库中的datetime类型:

from datetime import datetime

于是我们就可以定义各种时间类型的变量,这个并不复杂,比如datetime(2004, 10, 1)就表示2004年10月1日。

通过输出可以看出系统已经能够正确理解,并且显示出合适的格式。

有了这个时间类型的日期,就可以处理时间了。比如读取年份:

print(frame[‘birthday’].dt.year)

通过dt.year属性可以获取到具体的年份

其实有了这个真正的生日,我们就不该在数据存储年龄,毕竟年龄是会随着时间而改变,而生日,则就可以利用当前年份求的具体的年龄:

print(datetime.now().year – frame[‘birthday’].dt.year)

其中datetime.now()返回今天这个时间,其中的year属性就是今年。

看的出来,数据和年龄字段是对应的,

甚至可以利用这个信息来自动生成新的年龄列。

很多同学可能也觉得这种表示非常奇怪,因为前面这个year只获取当前今天的年份,而后面这个DataFrame获取的是所有记录的所有年份,1个数据怎么可以和这么多数据进行计算呢?其实,这也是Python的强大之处,Python会自动进行扩展,将这个一个当前今天的年份应用于所有所有记录所有年份的计算,而我们在很多时候只需大胆的尝试,很多情况下都可以成功!

我们也可以转时间转换为各种自定义的格式,以满足特定的用户要求。基本思路就是取出时间信息的各个组成部分,将其转换为字符串,并和其他字符拼凑成最终所需的格式:

print(frame[‘birthday’].dt.year.apply(str) + ‘年’ + frame[‘birthday’].dt.month.apply(str) + ‘月’)

这里就是拼凑出带有汉字年月的时间。

其中由于year不是字符串类型,因此需要调用str转换函数,对于DataFrame来说,每个列都是具有多个值,因此可以使用apply函数分别将列中的每个值都拿参数中的函数处理下,从而得到处理后的新列值。

当然,更多的情况是我们所获取的日期数据本身就是字符串,比如从网络数据中读取了文本数据等,因此我们需要将其转换为时间类型才能进行时间类型的处理。

# coding:utf-8
import pandas as pd
from pandas import DataFrame
from datetime import datetime

data = {'ID': ['000001', '000002', '000003', '000004', '000005', '000006', '000007'],
        'name': ['黎明', '赵怡春', '张富平', '白丽', '牛玉德', '姚华', '李南'],
        'gender': [True, False, True, False, True, False, True],
        'birthday': ['2004-10-01', '2000-11-27', '2002-01-29',
                     '2002-08-27', '2003-03-14', '2002-12-17',
                     '2004-04-12'],
        'height': [1.88, 1.78, 1.81, 1.86, 1.74, 1.75, 1.76]
        }
frame = pd.DataFrame(data)
print(frame)

其中我们增加了字符串类型的birthday生日列,去除了年龄列。为了计算年龄列,我们首先将生日列转换为时间类型,如:

print(pd.to_datetime(frame[‘birthday’]))

可以直接调用pandas提供的to_datetime函数来实现。

转换后即可还像刚才一样,读取其中的年份,进而完成年龄的计算。

frame[‘age’] = datetime.now().year – pd.to_datetime(frame[‘birthday’]).dt.year

要记得依然导入datetime类型。

这种to_datetime函数的功能很强大,即使你写成其他格式的日期,只要是常见的都能自动理解并解析成正确的日期数据。比如这里我们调整了日期格式,将年月日换成了月日年,程序依然运行正常。

不过如果格式确实比较古怪,比如在日期的年份后还专门加了个“年”的汉字,形成一种非标准的格式,此时就会出错。

对于此类问题,就需要利用datetime类中的strptime函数,这个函数可以指定原有字符串中日期的格式,从而实现对特定格式日期的转换。

# coding:utf-8
import pandas as pd
from pandas import DataFrame
from datetime import datetime

data = {'ID': ['000001', '000002', '000003', '000004', '000005', '000006', '000007'],
        'name': ['黎明', '赵怡春', '张富平', '白丽', '牛玉德', '姚华', '李南'],
        'gender': [True, False, True, False, True, False, True],
        'birthday': ['10-01-2004年', '11-27-2000年', '01-29-2002年',
                     '08-27-2002年', '03-14-2003年', '12-17-2002年',
                     '04-12-2004年'],
        'height': [1.88, 1.78, 1.81, 1.86, 1.74, 1.75, 1.76]
        }
frame = pd.DataFrame(data)
frame['age'] = datetime.now().year - frame['birthday'].apply(datetime.strptime, args=['%m-%d-%Y年']).dt.year
print(frame)

不过这个函数不能直接应用于多个记录值的DataFrame列,此时可以考虑使用apply函数和匿名函数来实现。大家可以这样理解,通过这个语法,我们可以把一个个DataFrame列的记录值分别传给strptime函数,其中我们可以看出它的第二个参数给出了特定的日期格式。其中的%m就表示其中的月份信息,其他类似。

一般来说,如果是标准日期格式,更为强大的函数可以使用parse函数,导入方法为:

from dateutil.parser import parse

此时即可通过apply函数将其应用于所有的时间类型转换。

print(frame[‘birthday’].apply(parse))

不管怎么转换,一旦转换为了时间类型,就可以继续按照时间来处理,甚至格式化处理成自己想显示的格式:

print(pd.to_datetime(frame[‘birthday’]).apply(datetime.strftime, args=[‘(%m):%d-(%Y)’]))

此时可以使用datetime的strftime函数,其中第二个参数可以说明需要转换过去的格式信息。

如果转换的格式含有汉字信息,就需要采取可以修改语言类型(locale),方法是导入locale库:

import locale

然后,设置语言类型为中文:

locale.setlocale(locale.LC_CTYPE,’chinese’)

print(pd.to_datetime(frame[‘birthday’]).apply(datetime.strftime, args=[‘(%m月):%d日-(%y年)’]))

还能够做一些对时间的处理,比如我们发现现有的学生生日因为时区关系,延迟了一天,需要将每个人的生日完后往前推一天。这个可以使用timedelta类型,为此需要首先导入:

from datetime import timedelta

然后就可以定义这种时间差值,并将其累加到现有的时间变量中去

print(pd.to_datetime(frame[‘birthday’]) + timedelta(days=-1))

最后,我们来看看关于分组的问题,比如我们想看看出生在不同年份的人数各有多少。

如果生日是datetime时间类型,我们可以直接写为:

print(frame[‘ID’].groupby(frame[‘birthday’].dt.year).count())

如果生日是字符串类型,就需要多做一步,先将日期转换为字符串,然后再一样的取年份再分组:

print(frame[‘ID’].groupby(pd.to_datetime(frame[‘birthday’]).dt.year).count())

这里我们综合运用了分组和取日期型变量的年份方法,大家可以自己尝试下。

Python大数据分析10:案例-买卖股票能赚多少钱?

这次我们准备结合一个股票数据集合来检验下我们这段时间学习的数据分析方法。

我们首先需要的是一个股票数据集合,在互联网上有很多不错的股票数据获取方法,这里我们使用的是Tushare,它是一个免费开源的Python财经数据接口包,其中就包括了个股历史交易数据。

那么该如何使用呢?方法是一样的,就像以前使用Pandas一样,我们首先需要加载Tushare库,方法仍然是在文件菜单中选择设置,在打开的界面中,选择当前项目的项目解释器,点击右上角的加号,添加新的外来库。输入tushare查询,并点击左下角的安装包按钮,即可安装使用。如果期间有问题,不妨多试几次即可。

安装好后以后,就以首先测试下是否能正常使用。方法很简单:

import tushare as ts

frame = ts.get_k_data(‘600339′, start=’2010-01-01’)

print(frame)

这里的第一行仍然是导入库,并起个别名为ts。然后通过调用它的get_k_data方法就以得到指定股票在指定时间范围内的行情信息。这里的k是指k线图的意思,就是股票行情的意思。这里我们选择了600339代码的股票,时间从2010年初开始。该方法很好用,直接返回一个DataFrame,因此可以直接输入查看。此时应该可以看到完整的结果。

一般的股票行情数据包括四个重要数值,分别是开盘价、收盘价、最高价、最低价,这里还有交易量。

我们不妨对前段时间学习的内容做些练习。

比如查询收盘价大于11元的所有交易记录。这是一种行的选择,我们可以表达为:

print(frame[frame[‘close’] > 11])

这个条件很简单,就是直接比较close收盘价字段即可。

那么我们很关注股票是否盈利,因此我们更为常见的查询是查询收盘价是否大于开盘价,也就是股票价格在上涨。刚才是和11比,这里只需和开盘价比较即可:

print(frame[frame[‘close’] > frame[frame[‘open’]])

大家看到错误了吗?这是一个初学者写的代码,大家能看出哪里出问题了吗?

错误有很多种,这种错误其实比较简单,是一种语法错误,也就是说,在形式上就肯定错了,通常PyCharm会提示你,这是语法错误(invalid syntax)。你会注意到一个红色的波浪线,那就是错误的位置。不过,要想看清是什么错误,并不容易。一般而言,这里是结束括号的地方,可能是括号不对应。

这里有一个简单的方法。不妨利用鼠标点击错误前面的方括号,它会对等提示前面对应的括号,没有问题。我们再往后移一个方括号,对等提示的前面对应括号,也没有问题。我们再往后移到最后一个圆括号,对等提示的前面对应圆括号,似乎没有问题。但是你仔细看看,当我们选中条件所在的起始方括号时,发现少了个表达条件的对应的结束方括号。利用这种方法可以有效的定位括号的匹配错误。

print(frame[frame[‘close’] > frame[frame[‘open’]]])

于是我们增加了结束方括号,那么仍然还是有错误。很多数据分析就是这样不断的在错误中积累经验,大家遇到问题时,也千万不要随意抛弃和立刻询问别人,找错的能力本身就是一种必需的能力,也只有不断的自己排查错误才能越找越快。

根据错误信息,这次不是语法错误了,而是语句书写错误,我们不妨使用空格区分下:

print(frame[   frame[‘close’]   >   frame[frame[‘open’]]   ])

大家看出问题了吗?你会发现其实我们画蛇添足,在条件比较的第二个列表示中多了一个Frame,这种错误很隐蔽,但是根本上还是需要大家熟悉功能和相应的表达方式。

于是,我们得到了最终正确的写法:

print(frame[frame[‘close’] > frame[‘open’]])

千呼万唤始出来,我们终于看到了结果。这些方法不论功能如何,其实都是数据选择,而且只是行的选择,我们所需要做的就是提供一种条件,让满足条件的行出现即可。

我们发现这些天的行情中,确实是每天都在涨,如果每天都在开盘买进100股票,然后收盘再卖出100股,由于股票买卖当天不能买进即卖出,我们可以假设你一开始就已经持有100股。那么能赚多少钱呢?

这个数据分析其实表现为要对收盘价和开盘价进行一种相减的运算,我前面也介绍过,你怎么想,就怎么做,那么我就直接将两个列减一下:

print(frame[‘close’] – frame[‘open’])

这种写法没有问题,细心的同学会注意到,怎么会有负数?因为这里我们并没有限定条件,是所有交易日的收盘价减去开盘价。即使如此,我们好像还很难看出来最终结果。

我们大胆的使用一种汇总求和的方法。

print((frame[‘close’] – frame[‘open’]).sum())

这其实是指对刚才所有输出的结果列进行汇总,sum为求和,居然真的算出来了,当然只有24元。可见每天随时买卖肯定意义不大。但是这是不对的!别忘了,这只是买卖1单位股票的交易情况,事实上,股市最少也要100股才能买卖,因此还需乘以100:

print((frame[‘close’] – frame[‘open’]).sum() * 100)

那就乘呗,还不错,即使每天买卖,还能赚2400元多。

那么假设我们知道哪些天能涨,只在那些天买卖100股,看看能赚多少钱呢?

这里的关键是要在前面我们选择后的数据上在进行计算和汇总,为此,我们采用了保存上次选择后的数据,然后再进行计算汇总:

newFrame = frame[frame[‘close’] > frame[‘open’]]

print(newFrame[‘close’] – newFrame[‘open’])

结果都是正数。

这里大家也请注意变量名称命名的方法,一般不要顺意使用frame1、frame2之类的方法,那些名称不能很好的表达真实的含义。现在的名称很清楚,如果有两个单词,一般习惯把第二个单词大写开头,第一个还是小写,这是一种常见的写法。

我们再汇总下:

print((newFrame[‘close’] – newFrame[‘open’]).sum()*100)

这次结果不错,即使只有100股,还是能赚一万六千多元。

可能有同学会说,看来股市只要坚持买卖一定能赚钱啊?千万不能这样想,因为股票交易本身还需要手续费,常见的规则是印花税占交易总额的千分之一,佣金5元,过户费1元,这笔开支如果每天都买卖的话,我们来看看会怎么样?建议大家自己想一想,再看看这里的答案:

这是每天买卖100股的成本:

print(((frame[‘close’] – frame[‘open’]) * 100 * 0.001 + 6).sum())

居然是一万四千多,虽然只能赚2400多。

不过,如果我们要是能知道哪天能涨的话:

newFrame = frame[frame[‘close’] > frame[‘open’]]

print(((newFrame[‘close’] – newFrame[‘open’]) * 100 * 0.001 + 6).sum())

这次我们需要花费七千三百多,相对于一万六千多元的盈利,我们最终还是赚了三千元左右。大家觉得可能吗?很简单,除非你知道每天一定是涨还是跌,我们想想还是老老实实去学Python大数据分析吧!

这个练习很有意思,希望大家多结合自己的专业和工作内容,有选择的针对自己的问题,尝试着利用Python数据分析方法来解决。

Python大数据分析3:Python基本使用

正如前面所说,我们这门课并不要求大家使用非常专业的编程方法来完成数据分析,因此课程并不打算对Python语言进行非常深入和完整的介绍。同学如果有兴趣,可以自行到其他Python入门课程中学习。如果没有时间,关系也不是非常大,我们在课程中会把最为需要的、最基本的一些内容随着课程进度介绍给大家。不过,我们依然会控制住难度和复杂度,只是希望大家要坚持练习,把我们课上所讲的通过实践上机来加以巩固,并尝试做更多的扩展练习。

既便如此,我们还是应当了解下一些基本的使用方法,包括Python的基本特点和PyCharm这个工具的一些功能。这些就是本次课程的主要内容。

首先我们必须理解一件事情,就是Python进行数据分析等工作,都是一条一条语句来实现,通常一条语句具有一个功能,因此通过罗列多条语句就可以实现多种功能组合起来的复合功能,非常类似于小孩搭积木。

还记得print这个函数吧,它就是输出信息,那么能不能输出我们所需要的其他内容呢?比如我们输出一个我们输入的文字,应该怎么做呢?

首先我们应该是输入,然后就是输出。输出我们会了,输入呢?也很简单,就是input函数:

input()

运行这个代码的时候,你会发现这时底下没有什么反映,但是有一个红色方块亮着,这说明程序正在等待,等待我们的输入。

我们在底下不妨随便输入些字符,回车确定下,你会发现红色没有了,程序似乎也结束了。刚才就是输入,下面显示的绿色文字就是你刚才输入的文字。那么能不能将其改变下呢?如比变成大写字符。

为此我们需要首先做一件事,那就是把刚才输入的内容保存下来,然后才能对其进行改变,比如变成大写。保存的方法非常简单,就是:

str = input()

很多初学者觉得不好理解,这个需要习惯。它不是指str等于input,而是从右向左看,表示输入完的input函数就把输入的信息给str这个变量,可以把变量理解为存储信息的一个载体。后面就可以利用这个变量得到刚才输入的信息。

比如我们不妨再次输出下:

str = input()

print(str)

屏幕中绿色文字是我们输入的文字,但是黑色的文字就是print再次输出的文字,显然此时print后面括号中就说明str具有刚才你输入的内容。

怎么变成大写呢?一般而言,你所遇到的各种问题,通常都有解决方法,而无需你去发明方法。一般有两种方法解决,一种是大胆的在这些变量后面按下点,通常都会出现很多内容,如这时你会发现有一个upper,这也是一个函数,见名知意,就是大写的意思。不过它归属于这个str变量,只能被str所使用,大胆的回车确定选中。

print(str.upper())

此时再次输入,可以发现以大写输出了内容。

这就是一种常见的方法,大家只需要在变量后按下点,即可查找下所需的功能。

但是有时小心会因为点击或者按动键盘而将这个好用的提示关闭了,此时有个方法,按下Ctrl+空格,你所喜爱的那个提示又出现了,这个功能就是PyCharm提供的,显然这也是好用的代码编辑器的特点。

另外一种方法就是直接利用其他函数,比如我们想输出你输入字符的个数,这就需要len这个函数:

print(len(str))

结果是正确的。这里的len也是一个函数,不过,它不属于str变量,此时可以通过将其放在len函数后面的括号中,当成len函数的参数。看到这里,大家感觉到了没,函数就像一个黑匣子,通过参数投入一个数据,出来一个数据。当然功能各不一样。

大家能看懂这个代码是什么意思吗?

str = input()

str = str.upper()

print(str)

其实意思是一样的,只是中间这一步先把字符转换为大写,并替换回了原有的str变量。大家从中也看出来了,所谓的Python编程就是这样,一行一行的书写,一行一个功能,就像堆积木一样,拼凑出完整的功能。

这里再补充一个极为有用的功能,常常代码写多以后,可能会乱。建议大家经常按下Ctrl+Alt+L,你就会发现PyCharm就自动布局排版代码,删除不必要的空格,对齐代码等等,非常方便。

有时候,我们会遇到一个问题,我们不知道有哪些函数?这就要学习了,遇到一个了解一个,久而久之,就能把一些最为常见的函数学习到。我们这门课大部分数据分析功能都是这些函数所提供的,我们无需知道细节,只需知道如何使用它们,完成我们所需要的数据分析任务就足够了。

有时候,这些函数可能还需要导入,因此,首先需要导入这个库,然后就可以使用这些其他的库了。这些内容后期大家可以通过课程学习来不断了解具体使用方法。

通过这种方式,Python可以将各种扩展库中的函数功能源源不断的增加进来,形成了庞大丰富的功能,最终方便我们的使用。