Python大数据分析1:数据分类分析(企业欺诈识别)

数据分类是一种机器学习的数据预测方法。

在这里,我们谈到的机器学习。大家可能会觉得有点畏惧,觉得是不是很难。我们首先需要对它有个简单的了解:第一,机器学习是一些计算机分析方法的统称,虽然很难,但是Python已经提供了很多很好用的库,我们只需学会如何使用即可,正如相机拍照原理和过程很复杂,但是我们只需按动那个快门键;第二,机器学习方法不同于前面我们介绍的一些基于现有数据进行总结性分析的方法,这些方法或者通过表格,或者通过可视化,提供给大家一种了解现有数据的分析途径。机器学习分析方法更强调智能性,甚至可以对未知数据进行预测分析,这些能力非常强大。

这次我们就先看看分类。在这里,数据分类有着明确的规定,即对于一组数据,我们事先对它们进行一种分类标注,然后通过一些诸如机器学习等分析方法,了解这种分类背后的数据规律,并由该机器学习分析方法自动预测新数据的类别。

我们从一个例子开始。加利福尼亚大学提供了一组免费的公开数据集合,其中提供了各种分析功能适用的数据:其中有三百多种分类目的的数据,我们选择分类数据,进一步选择其中的企业审计数据(Audit Data),此时点击Download后面的Data Folder数据文件夹,即可在打开的界面中选择Audit_Data.zip数据文件即可。

这个数据2015年至2016年间一些公司非机密数据,根据诸如环境状况报告、利润价值记录等信息,预测公司的欺诈可能性,共计26个数据列。这个可能性主要通过最后一列的Risk可以看出,为1表示存在风险,为0表示不存在风险。

接下来,我们需要解释一下如何进行分类分析。和所有其他机器学习方法差不多,都是一种常见的流程。我们接来下就按照这个流程来说明下:

首先是数据获取加载。

我们还是通过pandas读取数据:

import pandas as pd

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

print(frame)

第二步是数据预处理。不同的机器学习方法可能对数据有着不同的要求,比如一般的分类方法,要求所有参与计算的列必须是数值型,同时还可能要求数据不能有缺失的空值。

事实上,这个数据是有些小问题的。

首先我们可以观察下有无非数值型数据。具体Python处理方法我们后面专门来讲,这里我们就不做过多介绍。不过即使如此,我们也可以使用最为常见的Excel来查找下。

在Excel中按下Ctrl+A选中全部记录,按下Ctrl后再点击下第一行,你会发现选择内容去除了第一行的标题,在Excel中直接按下Ctrl+F查找,即可在选择的数据内容中查找,在定位中选择常量下的文本,去除其他选择项目,点击定位即可。此时可以看到已有三块数据被选中,为灰色,建议直接替换为0。同样的方法,在定位中选择空值,也可以快速看到存在的空值记录,建议使用0填充。到此为此,数据预处理手工完成。

第三步是数据划分。这里包括两个操作,一是数据列的划分,将现有数据分为特征列和预测列,也就是说,你准备按照什么数据进行分类,即特征列,这里应该就是除了最后一列外所有的企业数据特征列。分好的类在哪个列标记,即类别类,也是将来准备预测类别的列,因此也可以称之为预测列。这里就是最后一列,取值为0或者1的Risk。

写成代码的形式就是:

import pandas as pd

frame = pd.read_csv(‘C:\\temp\\audit_risk.csv’, header=0)

y = frame[frame.columns[len(frame.columns) – 1]]

frame.drop(frame.columns[len(frame.columns) – 1], axis=1, inplace=True)

X = frame

print(X)

这里首先取出最后一列,按照习惯,预测列名称通常为小写的y。其中这里我们没有使用列名的方法,故意使用了另外一种方法,即按照最后一列的序号来取出相应的列,根据当前列的个数减一可以得到最后一列的序号,因为从零开始表示第一列。然后根据这个序号得到相应的列,这种用法在没有列名的时候比较灵活。然后再删除最后一列,这里没有保存Frame,而且直接通过inplace参数直接在原有DataFrame上删除,axis为1表示以列为单位操作,而第一个参数正是最后一列的序号。按照习惯,特征列名称通常为大写的X。

第二个数据行的划分,主要目的是分为训练集和测试集合。所谓训练集合,是指使用哪些数据来训练分类算法。这些分类模算法必须在一些给定的数据中去训练,或者说去学习,才能自动获取最能反映分类的规则,并保存到分类模型中。一般会把整体数据的大部分作为训练集合,不能太小,否则训练效果会较差。所谓测试集合,主要是用来评估分类算法,看看拿刚才的模型来预测下,比如这里预测下分类结果,看看和已知的结果是否一致,显然越一致算法越好。通常这部分数据较少。到了真实应用场景下,就可以拿真正想预测的数据作为测试集合。

看起来很复杂,其实很简单,我们再次使用一个著名的Python库,叫sklearn,它是目前非常著名的一个实现各种机器学习方法的库。还像以前一样导入到项目,代码为:

import pandas as pd
from sklearn.model_selection import train_test_split

frame = pd.read_csv('C:\\temp\\audit_risk.csv', header=0)
y = frame[frame.columns[len(frame.columns) - 1]]
frame.drop(frame.columns[len(frame.columns) - 1], axis=1, inplace=True)
X = frame

X_train, X_test, y_train, y_test = train_test_split(X, y)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)

其中首先导入sklearn中专门用于划分训练集和测试集的train_test_split类型,自动获得四块内容,正好对于特征列和预测列、训练集和测试集的四种组合。

这里输出了下大小,正好看出,所有X相关的特征数据都是26列,所有训练和测试的数据比例划分是3:1,大家看懂了这个结果吗?

也可以通过设置test_size改变默认的分隔设置:

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1)

test_size默认为0.25,表示测试集的比例。

好了,到了最关键的一步,即第四步,数据训练。就是根据上述分好的数据,利用训练集合相关的两块数据来计算、来学习,得到分类模型。

代码为:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier

frame = pd.read_csv('C:\\temp\\audit_risk.csv', header=0)
y = frame[frame.columns[len(frame.columns) - 1]]
frame.drop(frame.columns[len(frame.columns) - 1], axis=1, inplace=True)
X = frame

X_train, X_test, y_train, y_test = train_test_split(X, y)
estimator = KNeighborsClassifier()
estimator.fit(X_train, y_train)

这里首先需要选择一个分类算法,我们选择了k近邻分类算法,生成一个分类器变量,记得此时需要导入这个分类算法。具体分类怎么实现的,我们无需过问,其实有很多不同的算法,后面我们还可以做出选择调整。

然后调用这个分类器变量的fit方法,里面正好就是训练集合的两块数据。

第五步,就是最后一步,效果评估。一般就是使用模型预测下,比如这里的分类预测,然后看看准确度。

代码为:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import numpy as np

frame = pd.read_csv('C:\\temp\\audit_risk.csv', header=0)
y = frame[frame.columns[len(frame.columns) - 1]]
frame.drop(frame.columns[len(frame.columns) - 1], axis=1, inplace=True)
X = frame

X_train, X_test, y_train, y_test = train_test_split(X, y)
estimator = KNeighborsClassifier()
estimator.fit(X_train, y_train)
y_predicted = estimator.predict(X_test)
print(np.mean(y_test == y_predicted))

这里仍然分类器的predict方法直接根据测试集的特征数据来预测。

如果进行效果比较呢?这里我们也无需直接去做,再次使用一个著名的科学计算库numpy,也记得导入,并在项目代码中导入,即可利用它的mean,即平均值,我们只需将预测的结果y_predicted和已知的y_test以是否相等作为条件,看看正确的占比有多少。此时,mean这个方法的参数是一个由真假组成的列表,它会自动计算True在其中的比例,作为平均值。这个预测结果的准确度还是挺高的!95%!

为了好看,我们可以再次改变下输出的格式:

print(“准确度为:{:.1f}%”.format(np.mean(y_test == y_predicted) * 100))

这里使用了字符串的格式化输出功能,其中整个字符串都会直接输出,而花括号括起来的部分,会以format函数中的结果来展示,而花括号里的内容就表示按照一位小数输出。

Python大数据分析9:案例-谁是一百年前南方小镇的社交明星?

这次我们根据一个有趣的数据,来看看如何使用网络可视化分析方法。网络可视化分析方法可以让我们以一种全新的眼光来看待平淡无奇的数据,并找到新的发现。

一些社会学家和种族学家在20世纪30年代做了一个很小的数据实验,实验目标是描绘出美国南部一个小镇一组女士的社交结构关系,他们使用的数据是报纸上公开发表的数据,共有18位女士参加了14个不同的社交活动。接近100年过去了,我们今年所能知道的仅仅只有这些数据。这些数据描述了这些女士分别参加了哪些社会活动。

为了方便生成适合网络分析的数据结构,我们需要对数据进行必要的处理。原始数据中有两类数据,一类是女士,一类是活动。一般比较常见的网络图都是由一种类型的数据节点构成,同时由于我们需要知道女士之间的联系,因此需要建立女士之间的关系。

我们可以假设如果两个女士参加一个活动,就意味着两人存在着一个联系,

共同参与的越多,联系应该越紧密。为此我们先来生成女士之间的联系:

# coding:utf-8

import pandas as pd

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

frame = pd.merge(frame, frame, left_on=’Activity’, right_on=’Activity’)

print(frame)

这里使用了数据连接的方法。通过将现有的数据自己和自己连接,按照左表和右表活动相等即可连接全部记录。

从结果可以看到,诸如刚才的W01女士和W02女士都建立了连接。这里数据有几个特点:第一,这里至少一半的数据冗余,因为自己会和自己连接,而且W01女士和W02女士连接,就会有W02女士和W01女士连接;第二,确实不同女士可以因为参加同一活动而被连接起来,这种连接会随着共同参与活动的多少而有总数的变化。

我们先处理第一个问题:

frame = frame[frame[‘Lady_x’] < frame[‘Lady_y’]]

print(frame)

这里使用了一个小技巧,我们利用女士号码小于的关系筛选了记录,可以看出但凡等于和大于的都被去除,正好对应自己和自己的连接,以及前后错位的冗余连接。七百多条记录只剩下三百多记录。

再看第二个问题,两位女士共同参加的活动越多,就会形成更多的连接,因此可以对现有连接结果按照连接女士汇总下:

frame = frame[[‘Activity’]].groupby([frame[‘Lady_x’], frame[‘Lady_y’]]).count()

print(frame)

此时,就是按照两个连接女士的号码作为分组条件,汇总统计各自的总数。看的出来,确实存在较大的差异,数值越高,女士的关系应该更为紧密。

此时取出索引的值,生成列表,即可得到网络图所有边的信息:

edges = frame.index.tolist()

print(edges)

对于节点,我们可以在未汇总前直接统计所有女士:

frame = frame.groupby(frame[‘Lady’]).count()

nodes = frame.index.tolist()

print(nodes)

这里仍然利用了分组的方法,取出了分组的所有索引,并生成了一个节点列表,供后续网络图生成时使用。

到此为止,我们已经完成了所需数据内容的整理。这也是数据分析的关键内容,一定要根据分析的目标和方法的特点,有选择的进行数据进行必要的处理,通常这一块是最能体现数据分析特点的地方,不同的整理方法往往也会产生不同的最终分析效果。

将上述两个代码融合下:

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

frame = pd.read_csv('C:\\temp\\SouthernLadies.csv')
frame1 = frame.groupby(frame['Lady']).count()
nodes = frame1.index.tolist()

frame2 = pd.merge(frame, frame, left_on='Activity', right_on='Activity')
frame2 = frame2[frame2['Lady_x'] < frame2['Lady_y']]
frame2 = frame2[['Activity']].groupby([frame2['Lady_x'], frame2['Lady_y']]).count()
edges = frame2.index.tolist()

g = nx.Graph()
g.add_nodes_from(nodes)
g.add_edges_from(edges)
nx.draw(g)
plt.show()

这里前面的代码就是生成节点列表和边列表,为了反复使用数据,可以看出我们保留了最初的DataFrame,通过存储的frame1和frame2分别计算了节点和边。

最后的部分就是绘图,其实绘图并不复杂,利用导入的networkx,生成图,并添加节点和边,绘制即可,注意networkx不能独立使用,必须配合matplotlib,因此最后不要忘了pyplot的show方法。

这是绘制的结果,显然还需要进一步调整,比如增加必要的标签显示和颜色美化。

nx.draw(g, with_labels=True, node_shape=”s”, alpha=0.9, node_color=’green’,

        node_size=1500, font_size=16, font_weight=”bold”, font_color=’white’)

plt.show()

这些属性大都见名知意,大家可以自行调整。

从中可以看出,有些节点处于一种社交中心的特点,比如W09、W14和W15等。

但是,这里的边都是一样的粗细,无法看出前面我们利用共同参加活动所计算出来的重要性关系。

于是,我们再次使用加权的边来绘制。为此需要做两步:第一步是将权值也保存到列表中:

frame2 = pd.merge(frame, frame, left_on='Activity', right_on='Activity')
frame2 = frame2[frame2['Lady_x'] < frame2['Lady_y']]
frame2 = frame2[['Activity']].groupby([frame2['Lady_x'], frame2['Lady_y']]).count()
weights = frame2['Activity'].tolist()
edges = frame2.index.tolist()

g = nx.Graph()
g.add_nodes_from(nodes)
g.add_edges_from(edges)
nx.draw(g, width=weights, edge_color=weights,
        with_labels=True, node_shape="s", alpha=0.9, node_color='green',
        node_size=1500, font_size=16, font_weight="bold", font_color='white')
plt.show()

首先我们将总数统计列保存为一个列表weights,显然和它和边列表元素个数一样,并且一一对应。然后,我们再draw函数中设置width属性即可表达每条边的宽度和颜色,以体现这个权值。

图中可以看出,W03和W04等节点呈现出比较明显的、与其他节点的强关联性,浅色权值更高,说明她们的好伙伴更多一些,并且隐隐约约还能看到两个高度关联的团体。

为此我们需要将无关的低权值边去除,以便更高的看清结果。

我们在分组后增加一条删除汇总个数较低的边,比如小于4:

frame2 = frame2.drop(index=(frame2.loc[(frame2[‘Activity’] < 4)].index))

weights = frame2[‘Activity’].tolist()

edges = frame2.index.tolist()

从结果来看,虽然更能看到这两个社交团体,但是由于存在很多无关节点,默认布局很不好看。

因此我们需要去除这些已经不再连接边的独立节点。我们先使用一种最为直观的方法,就是从边中直接得到需要显示的点:

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

frame = pd.read_csv('C:\\temp\\SouthernLadies.csv')
frame2 = pd.merge(frame, frame, left_on='Activity', right_on='Activity')
frame2 = frame2[frame2['Lady_x'] < frame2['Lady_y']]
frame2 = frame2[['Activity']].groupby([frame2['Lady_x'], frame2['Lady_y']]).count()
frame2 = frame2.drop(index=(frame2.loc[(frame2['Activity'] < 4)].index))
weights = frame2['Activity'].tolist()
edges = frame2.index.tolist()

frame2 = frame2.reset_index()
nodes1 = frame2.drop_duplicates('Lady_x')['Lady_x'].tolist()
nodes2 = frame2.drop_duplicates('Lady_y')['Lady_y'].tolist()
nodes = nodes1 + nodes2

g = nx.Graph()
g.add_nodes_from(nodes)
g.add_edges_from(edges)
nx.draw(g, width=weights, edge_color=weights,
        with_labels=True, node_shape="s", alpha=0.9, node_color='green',
        node_size=1500, font_size=16, font_weight="bold", font_color='white')
plt.show()

我们去除了最早的节点生成方法,利用当前索引中已有的节点,通过去除Lady_x和Lady_y重复的元素,转换为列表再拼装,虽然有重复,但是问题不大,得到最终只有这些权值大于等于4的边才有的节点。

进一步调整边权值的阈值为5,可以发现更为明显,两个高度关联的女士社交团体就被识别了出来。

进一步调整边权值的阈值为3,又可以发现这两个团体是如何关联的,看得出来,一号团体的W03、W09,和二号团体的W10、W13和W14,是形成这两个团体联系的关键人物。

当然这样做,效果不错,但是获取点的方法太麻烦了,其实更为有效的方法可以直接从边中获取所有的边和节点:

其中我们直接将索引恢复到原有数据中,形成包括Lady_x、Lady_y和统计列的三列数据。

print(frame2.head(1))

frame2 = frame2.reset_index()

print(frame2.head(1))

这样就可以直接利用这个完整的DataFrame来生成网络图。

这是综合后的代码:

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

frame = pd.read_csv('C:\\temp\\SouthernLadies.csv')
frame2 = pd.merge(frame, frame, left_on='Activity', right_on='Activity')
frame2 = frame2[frame2['Lady_x'] < frame2['Lady_y']]
frame2 = frame2[['Activity']].groupby([frame2['Lady_x'], frame2['Lady_y']]).count()
frame2 = frame2.drop(index=(frame2.loc[(frame2['Activity'] < 3)].index))
weights = frame2['Activity'].tolist()

frame2 = frame2.reset_index()
edges = frame2.values.tolist()
g = nx.from_pandas_edgelist(frame2, 'Lady_x', 'Lady_y', create_using=nx.Graph())

nx.draw(g, width=weights, edge_color=weights,
        with_labels=True, node_shape="s", alpha=0.9, node_color='green',
        node_size=1500, font_size=16, font_weight="bold", font_color='white')
plt.show()

其中关键的语句就是from_pandas_edgelist方法,里面很清楚的给出边的起始节点列和终止节点列,绘图会自动绘制相关边和点。图形显示和以前没有区别,但是代码更为简单。

最后让我们把布局再美化下:

fig = plt.figure()

nx.draw(g, pos=nx.circular_layout(g),

        width=weights, edge_color=weights,

        with_labels=True, node_shape=”s”, alpha=0.9, node_color=’green’,

        node_size=1500, font_size=16, font_weight=”bold”, font_color=’white’)

fig.set_facecolor(“#000000”)

plt.show()

这里才有了黑色背景,请注意fig获取必须在draw之前进行才有效果,set_facecolor方法设置为黑色,这里颜色还是以前介绍的表示方法,红绿蓝都为0,为纯黑色。

另外,还采取了新的布局模式,circular_layout表示环形布局。

Python大数据分析8:案例-各省的人都往哪儿去?

这次我们结合人口流动的数据来看看我国人口在各省之间的移动特点,并据此从人口流动的角度,来分析下我国各省的相关性。当然,这里主要利用的还是各种pyecharts可视化分析方法。

首先我们加载人口迁移数据:

# coding:utf-8

import pandas as pd

frame = pd.read_csv(‘C:\\temp\\population.csv’, encoding=’GBK’)

print(frame)

该数据有三列,分别是流出省份、流入省份和人口流动数量,共计840条数据。这些数据都是从1985年到1990年共计5年的人口汇总统计数据。

为了更好的了解数据,我们首先对数据按照人口流动数量降序排列输出来观察下:

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

print(frame.sort_values(by=[‘count’], ascending=False))

我们发现,数据呈现快速下降的特点,我们可以选择大于10万的记录即可展示最为主要的流动关系。

有时候,可能会有这样的输出,这种情况并非错误,如果pandas在解析数据时,将最后count理解为字符,那么确实就是这个次序。对于字符而言,首先比较左边第一个字符,谁大就谁大,后面就不再比较,只有左边第一个字符一样,才会比较第二个,仍然按照刚才规则进行。

此时可以考虑强制转换为整数列:

frame[‘count’] = pd.to_numeric(frame[‘count’])

print(frame.sort_values(by=[‘count’], ascending=False))

这里的pandas自带的to_numeric也可以完成字符向数字的转换。

我们首先还是以地图可视化来展示下效果。这里需要展示的是一种省份之间的流向关系,因此需要使用线条来表示:

# coding:utf-8
import pandas as pd
from pyecharts.charts import Geo
from pyecharts import options as opts
from pyecharts.globals import ChartType

frame = pd.read_csv('C:\\temp\\population.csv', encoding='GBK')
results = frame[frame['count'] > 100000][['from', 'to']].values.tolist()
geo = Geo()
geo.add_schema(maptype="china")
geo.add("", results, type_=ChartType.LINES, linestyle_opts=opts.LineStyleOpts(curve=0.2))
geo.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
geo.render()

这里注意几个问题:

一是我们得到了人口流动数量大于10万的记录,并将这些数据设置为显示数据;

二是在图样显示中,我们将类型设置为线条,并通过curve属性将其设置为曲线。

从图中可以看出,这里实际显示的箭头是动态的,四川和黑龙江是人口流出大省,其中北京、上海、广东都是一些人口流入的重要地区。

我们再以另外一个角度来展示分析效果。比如就看流入情况:

# coding:utf-8
import pandas as pd

frame = pd.read_csv('C:\\temp\\population.csv', encoding='GBK')
results = frame[['count']].groupby(frame['to']).sum()
results = results.reset_index()
results = results.values.tolist()
print(results)

以人口流入数量按照省份汇总,形成每个省份的总数据。这里要注意一个问题,由于汇总后省份列变成了索引,因此通过reset_index按索引恢复过来,形成可以直接转换为list的数据格式。

这次我们以热点图的类型展示了结果:

# coding:utf-8
import pandas as pd
from pyecharts.charts import Geo
from pyecharts import options as opts
from pyecharts.globals import ChartType

frame = pd.read_csv('C:\\temp\\population.csv', encoding='GBK')
results = frame[['count']].groupby(frame['to']).sum()
results = results.reset_index()
results = results.values.tolist()
geo = Geo()
geo.add_schema(maptype="china")
geo.add("", results, type_=ChartType.HEATMAP)
geo.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
geo.set_global_opts(visualmap_opts=opts.VisualMapOpts(max_=1300000))
geo.render()

再次看出我们人口流入大省的分布情况,仍然主要集中在华南、华东和华北三块,依次递减。

接下来我们准备换个思路来看这个问题,通过人口在不同省份之间的流入流出,其实也能在一定程度上反映省份的关系,我们这次结合网络图来重新分析下省份之间的关系。

网络图其实需要两个基本数据,一个是节点,一个节点之间的边。为此我们来整理下数据。

首先我们得到边,然后根据边确定节点。考虑到界面显示有限,显示太多边会导致无法看清内容,因此我们做了必要的筛选,

# coding:utf-8

import pandas as pd

frame = pd.read_csv(‘C:\\temp\\population.csv’, encoding=’GBK’)

frame = frame[frame[‘count’] > 50000]

links = frame[[‘from’, ‘to’]].rename(columns={‘from’: ‘source’, ‘to’: ‘target’}).to_dict(orient=’records’)

print(links)

这里我们先过滤到现有人口流动数量不高于5万的省份流动记录。对于pyecharts,它也提供了专门的Graph类型用于绘制网络图。但是它要求对边做出固定格式的定义,从结果来看,是一种字典格式。因此,我们通过调用rename改名将列名换为source和target,这里的名称就表示源和目标,然后利用to_dict生成特定的字典格式。可以从结果看出,里面的信息很明确。

我们再得到节点:

results = frame[[‘count’]].groupby(frame[‘to’]).sum()

results[‘name’] = results.index

results[‘count’] = results[‘count’] / 50000

results = results.rename(columns={‘count’: ‘symbolSize’})

nodes = results.to_dict(orient=’records’)

print(nodes)

这里首先根据流入人口汇总,为方便索引的身份信息也转换为后续的字典内容,因此和以前一样,新增列保存为索引一样的内容,同时,新列名称必须为name,表示节点名称,而汇总的人数作为权值,可以利用节点大小来反映,为此需要将列名改为symbolSize,同时由于数值太大,因此可以考虑适当缩小,这里采取了除以最小数值的方法。

有了上述基础数据,我们就以完成绘制:

# coding:utf-8
import pandas as pd
from pyecharts.charts import Graph

frame = pd.read_csv('C:\\temp\\population.csv', encoding='GBK')
frame = frame[frame['count'] > 50000]
links = frame[['from', 'to']].rename(columns={'from': 'source', 'to': 'target'}).to_dict(orient='records')
results = frame[['count']].groupby(frame['to']).sum()
results['name'] = results.index
results['count'] = results['count'] / 50000
results = results.rename(columns={'count': 'symbolSize'})
nodes = results.to_dict(orient='records')
graph = Graph()
graph.add("", nodes, links)
graph.render()

首先要导入Graph类型,生成Graph类型变量,然后add函数中指定节点和边,第一个参数是标题。

从中可以明显看出,从人口流动关系来看,我国不同省份之间的联系。有几个明显的特征:比如以辽宁为中心的北方各省形成一个相对闭合的关系,四川为人口流动的大省,并通过河北和北方其他几个省市建立了联系,并通过江苏和华东几省也建立了联系。

为了增强效果,可以进一步增加显示范围,同时增加边的样式:

graph = Graph(init_opts=opts.InitOpts(width=”1600px”, height=”800px”))

graph.add(“”, nodes, links, linestyle_opts=opts.LineStyleOpts(width=0.5, curve=0.3, opacity=0.7))

graph.render()

这些都可以自由调整。

也可以改变显示模式,改为圆盘显示:

graph.add(“”, nodes, links, layout=”circular”)

graph.render()

结果展示方式变成了新的模样。不同的效果可以根据显示数据的分析要求来选择。

Python大数据分析7:案例-看看我国各省的GDP

地图绘制也是一种有效的可视化方法,它可以直观的将数据内容通过文本和颜色等不同方式展示在地图上,形成一种有效的呈现方式。

Python地图有很多库可以使用,我们这次使用的是著名的pyecharts,它可以更方便的实现地图的绘制。

为此,我们首先还是需要在设置上导入pyecharts库,具体过程同前面所述一致。

我们先绘制一个简单的地图看看。

from pyecharts.charts import Geo

geo = Geo()

geo.add_schema(maptype=”china”)

geo.render()

首先导入Geo类型,这个类型就是绘制地图的关键类型。创建它的变量后,调用add_schema方法指定地图类型为中国,即可render绘制。

此时会在当前项目中生成一个render.html文件,可以右击选择“在浏览器打开”,选择合适的浏览器即可看到结果。

之所以在浏览器才能查看结果,是因为这个是利用网页方式提供绘图功能。

这个地图已经可以显示动态效果,比如停留某个身份可以变亮,

滚动鼠标滑轮还能缩放地图。

好了,我们接下来准备数据。关于各省的GDP数据,可以考虑使用中国国家统计局的官方数据。在地区数据中,可以选择分省年度数据。

在地区数据中,可以选择全部地区。

此时登录即可免费下载,可以选择CSV格式。

下载数据的格式比较多,因此建议大家首先先简单处理下,将无关的一些格式信息去除。比如这里的头三行,

还有最后几行,都可以删除,形成标准的二维数据表格。

我们首先还是利用DataFrame读取下数据。

# coding:utf-8

import pandas as pd

frame = pd.read_csv(‘C:\\temp\\分省年度数据.csv’, encoding=’GBK’)

print(frame)

这里显示的是10年的数据。

我们读取其中最近一年2019年的数据,这里要注意一个问题,那就是pyecharts绘制地图需要一个二维列表结构的数据,因此我们需要读取地区和年份后,通过tolist方法转换为对应的格式:

print(frame[[‘地区’, ‘2019年’]].values.tolist())

现在就可以将其绘制在地图上:

# coding:utf-8
import pandas as pd
from pyecharts.charts import Geo

frame = pd.read_csv('C:\\temp\\分省年度数据.csv', encoding='GBK')
geo = Geo()
geo.add_schema(maptype="china")
geo.add("", frame[['地区', '2019年']].values.tolist())
geo.render()

这里的add函数就是增加数据,第一个参数表示图表名称,我们留空,第二个就是数据内容。这其实是把刚才的两段内容结合在了一起,但是报错了。什么错误呢?其实很简单,地区名称对应不上,原因在于pyecharts中对部分省份的标注与国家统计局不一致,比如“广西壮族自治区”应该改为“广西省”等等。

这里其实只需修改三个省份数据即可。大家可以自行根据错误提示进行修改,这也说明数据分析之前数据整理是非常必要的环节。

再次运行下,就可以在生成的网页中看到结果,

也可以刷新下这个网页来看。中心点大约是省会的位置。

这个网页具有较强的交互性,移动和悬浮鼠标都可以看到不同的变化。

默认情况下,地图上显示的数据内容过多。我们可以酌情去除。

from pyecharts import options as opts

geo.set_series_opts(label_opts=opts.LabelOpts(is_show=False))

geo.render()

这个设置就是表示将当前标签上的数据内容不予显示。这里不要忘记导入options类型,在pyecharts中,图表的一切皆通过options来修饰调整。

但是这时无法看出这些不同数据的直观特点,因此我们可以通过添加颜色来标注数据的大小:

geo.set_global_opts(visualmap_opts=opts.VisualMapOpts(max_=110000))

geo.render()

这里面的max_参数就表示最大值。

由于这些数据分布并不平均,甚至可以自己来定义不同的颜色区间:

geo.set_global_opts(visualmap_opts=opts.VisualMapOpts(
    is_piecewise=True,
    pieces=[
        {"min": 0, "max": 10000, "label": "1", "color": "blue"},
        {"min": 10001, "max": 20000, "label": "2", "color": "cyan"},
        {"min": 20001, "max": 50000, "label": "5", "color": "green"},
        {"min": 50001, "max": 80000, "label": "8", "color": "yellow"},
        {"min": 80001, "max": 100000, "label": "10", "color": "orange"},
        {"min": 100001, "max": 200000, "label": "20", "color": "red"}
    ]
))
geo.render()

这里看起来复杂,其实很简单,就是设定了is_piecewise属性表述分段,同时通过pieces属性后面的字典数据给出了每个段的上下限、标签和颜色。

显然这样更为灵活和定制化。

默认的外观比较简单,我们可以来看下。

比如对于点的形状,就可以通过设置来改变:

geo.add(“”, frame[[‘地区’, ‘2019年’]].values.tolist(), type_=ChartType.EFFECT_SCATTER)

geo.set_series_opts(label_opts=opts.LabelOpts(is_show=False))

geo.set_global_opts(visualmap_opts=opts.VisualMapOpts(max_=110000))

geo.render()

这里的add函数增加了type说明。

这个点会自动呈现变化的动态效果。

甚至可以在add函数中增加颜色样式,设置地图的色彩:

geo.add_schema(maptype=”china”, itemstyle_opts=opts.ItemStyleOpts(color=”#DD0000″, border_color=”#FFFFFF”))

这里面color表示地图背景色,而border_color表示边的颜色。

效果呈现出火热的中国红。大家可能会注意到这里的颜色很奇怪,这其实是计算机表示颜色的一种简单有效方法,

里面有三个部分,每个部分最小为00,最大为FF,这是十六进制的最大值,分别对应红绿蓝,所以边框三色全满,就是白色,而地图背景色绿蓝皆为无,红色淡了一些,形成略浅一些的中国红。大家可以自己大胆的调配看看效果。

也可以使用Map来填充

# coding:utf-8
import pandas as pd
from pyecharts.charts import Map
from pyecharts import options as opts

frame = pd.read_csv('C:\\temp\\分省年度数据2.csv', encoding='GBK')
map = Map()
map.add("", frame[['地区', '2019年']].values.tolist(), "china")
map.set_global_opts(visualmap_opts=opts.VisualMapOpts(max_=110000))
map.render()

这里使用了Map类型,基本用法差不多,主要区别在于add中第三个参数指定地图位置。

该类型使用颜色填充的方式,而不是点的方式,来显示不同数据的情况。

这里要注意一个细节,就是数据需要整理下,Map使用的中国各省份和Geo不一样,需要将全部的省、市、自治区等去掉。于是这里我们保存为“分省年度数据2.csv”文件。

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”)