2、偏差图
偏差图是单个特征中所有值与特定值之间的关系图,它反映的是所有值偏离特定值的距离。典型的偏差图有:发散型条形图,面积图,…
我们什么时候需要偏差图呢?
1.数据探索&数据解读
探索某一特征的分布,探索该特征偏离某个特定值(均值,方差等)的程度。
2.结果展示&报告呈现:
直观地展示某个特征的分布特征,快速得出结论。
2.1 发散条形图(Diverging Bars)
如果你想根据单个指标查看项目的变化情况,并可视化此差异的顺序和数量,那么发散条形图是一个很好的工具。它有助于快速区分数据中的组的性能,并且非常直观,可以立即传达这一点。
- 横坐标:里程
- 纵坐标:各品牌汽车
- 颜色:<0显示红色,>0显示绿色
我们的目标就是绘制出这张图,并且利用现有数据解读图内信息。
1、导入需要的绘图库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
#如果用jupyter notebook则需要这行代码让你的图像显示,如果是jupyterlab则不需要
2、先来认识一下绘制发散性条形图的函数
plt.hlines()
plt.hlines()表示水平的条形图,类似的还有垂直的条形图plt.vlines()
参数说明:
y:y轴索引
xmin:每行的开头
xmax:每行的结尾
colors:颜色,默认为“k”(黑色)
linestyles:线的类型,可选择{‘solid’,‘dashed’,‘dashot’,‘dotted’}
label:标签,默认为空
*linewidth:线的宽度
*alpha:色彩饱和度
绘制一个超级简单的条形图
#定义一个简单的数据
x = np.random.rand(10) #随机生成10个[0,1)的数字
x
#绘图
plt.hlines(y=range(10),xmin=0,xmax=x);
与我们的目标图像相比,有什么区别吗?
- 目标图像时从大到小顺序排序的
- 目标图像的线比较宽
- 目标图像是基于某个特定值将数据分成两部分,并用不同的颜色表示
(1)让图像顺序排列
使用sorted()函数,或者使用.sort()方法
a = sorted(x,reverse=True) #默认从小到大排列,reverse=True可以实现从大到小的排列,不会改变原数据的顺序
x.sort() #从小到大排列,并且会直接改变原数据
(2)设置plt.hlines(linewidth=5)用于将图像的线变宽点。
(3)让图像基于均值分成两部分
方法:使用 x = x-x.mean()即可让图像的均值变为0
(4)让两部分显示不同的颜色
直接使用color=[‘green’,'red]可以吗?答案是不可以。
我们需要根据数据建立与数据长度相同的颜色列表(使用for循环/列表推导式)
#创建颜色列表
#方法1.使用for循环
colors = []
for i in x:
if i<0:
colors.append('red')
else:
colors.append('green')
#方法2.使用列表推导式
colors = ['red' if i<0 else 'green' for i in x]
(5)让颜色变浅一点
参数alpha是颜色饱和度,默认为1,alpha的取值范围是[0,1],越接近1颜色越饱和,也就是颜色越鲜艳。
所以我们可以让alpha=0.5
到此为止,绘制发散型条形图的基本知识就差不多全部讲完了,下面我们使用完整的数据来实现我们的目标图像。
#认识绘图所需要的数据集
df = pd.read_csv("E:\old_computer\考研资料\项目准备\画图\菜菜和菊安酱的Python可视化50图{逆人行}\第一部分\第1章 课程介绍 + 可视化模块安装\课时 2 数据集\data\data\mtcars.csv")
df.head()
df.shape
df.info()
sum(df.cars!=df.carname)
df.columns
### 对目标数据进行格式变换
#### 提取出目标数据并进行标准化处理
x= df.loc[:,'mpg']
x.shape
#进行z-score变换
df['mpg_z'] = (x-x.mean())/x.std()
df['mpg_z'].values
colors=[]
for i in df["mpg_z"]:
if i <0:
colors.append("red")
else:
colors.append("green")
#写成列表推导式
df["colors"] = ["red" if i <0 else "green" for i in df["mpg_z"]]
#对数据进行排序
df.sort_values('mpg_z',inplace=True)
#排序之后索引也会跟着一起打乱,所以需要重置索引
df.reset_index(drop=True,inplace=True)
#绘制目标图像
plt.figure(figsize=(14,10),dpi=80)
#绘制
plt.hlines(y=df.cars,xmin=0,xmax=df.mpg_z,color=df.colors,alpha=0.4,linewidth=5);
#添加装饰
plt.ylabel('$model$',fontsize=15)
plt.xlabel('$mileage$',fontsize=15)
plt.yticks(fontsize=12)
plt.title('Diverging Bars of Car Mileage',fontdict={'size':20,'color':'k'})#设置图形变标题
plt.grid(linestyle='-.',alpha=0.5)#配置网格线
图像解读
(1)相同油耗内里程数最小的是林肯大陆 (Lincoln Continental)
这款车一开始是福特汽车公司总裁Edsel Ford的私人车辆
从1939年至今已经更新十代
林肯系列车走的是高端路线,油耗里程已经不再是它关注的重点
(2)位于均值线上的是法拉利迪诺(Ferrari Dino)
“Dino”品牌的诞生是为了推出一款价格较低,“价格合理”的跑车
“Dino”这个名字是为了纪念这位创始人已故的儿子Alfredo“Dino”Ferrari,他因设计用于汽车的V6发动机而受到赞誉
(3)油耗里程数最大的是丰田卡罗拉(Toyota Corolla)
这是丰田生产的一系列超小型和紧凑型轿车
从1966年至今,已经更新十二代
卡罗拉于1966年推出,是1974年全球最畅销的汽车,自那时起成为全球最畅销的汽车之一
(4)从图形上来看,这32款汽车根据相同油耗内的里程数被分成了两组
一组高于平均值,从图中可以看出基本上都是一些民用车(除了那款保时捷914之外)
一组低于平均值,基本上都是高端车,要不就是跑车,要不就是商用车
(5)大胆猜想:发散型条形图完成了聚类功能
用kmeans聚类验证发散型条形图聚类效果
#根据发散型条形图结果给原数据集添加标签·
df["label"]=[1 if i=="red" else 0 for i in df.colors]
df.shape
#使用kmeans算法对原数据集进行聚类
#导入kmeans库
from sklearn.cluster import KMeans
#读入原数据集
df1 = pd.read_csv("E:\old_computer\考研资料\项目准备\画图\菜菜和菊安酱的Python可视化50图{逆人行}\第一部分\第1章 课程介绍 + 可视化模块安装\课时 2 数据集\data\data\mtcars.csv")
#提取出目标数据,由于sklearn中不接受一维数据,因此需要对目标数据进行reshape操作
data_x = df1.mpg.values.reshape(-1,1)
data_x.shape
#>>>(32, 1)
#进行聚类
cluster = KMeans(n_clusters=2,random_state=420).fit(data_x)
cluster.labels_
#>>> array([0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
# 1, 1, 1, 0, 0, 0, 1, 1, 1, 0])
#新建一列标签列,将聚类的结果作为标签
df1["label"]=cluster.labels_
df1["label"].shape
#由于我们前面的发散型条形图对数据进行了排序与z-score操作,所以在这里对df1数据集进行相同的操作
df1.sort_values('mpg',inplace=True)
#重置索引
df1.reset_index(inplace=True)
#现在判断KMeans的标签结果与经过条形图后的标签结果 相同的个数有几个
sum(df1["label"]==df["label"])
#>>> 31
#准确率
score = sum(df1["label"]==df["label"])/df1["label"].shape[0]
score
#>>> 0.96875
#查看唯一聚类错的数据
df[df["label"]!=df1["label"]]
#这是中间的那个数,确实比较容易分类错
2.2 发散型文本(Diverging Texts)
分散的文本类似于发散型条形图,如果你想以一种漂亮和可呈现的方式来希纳是图表中每个项目的价值,那么它是一个比较合适的方式。
这和我们上面讲到的发散型条形图很相似,唯一不同的地方就是每个条形图上多了带颜色的文字
1、导入需要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
#如果用jupyter notebook则需要这行代码让你的图像显示,如果是jupyterlab则不需要
2、来认识一下绘制文本的函数
plt.text()
参数说明:
- x,y:放置文本的位置。默认情况下,就是数据坐标
- s:要显示的文本内容
- fontdict:用于覆盖默认文本属性的字典。fontdict的默认值是None,默认值由rc参数决定
#定义数据
x = np.random.rand(1)
y = np.random.rand(1)
#创建画布
plt.figure(figsize=(8,4))
#绘制图形
plt.text(x=x,y=y,s="text"
,fontdict={'size':20,"color":"b"});
#plt.text()函数一次只能标准一个位置,如果想一次添加多个文本,-->写循环
plt.figure(figsize=(8,4))
#绘制图形
for i in range(10):
x = np.random.rand(1)
y = np.random.rand(1)
plt.text(x,y,"text",fontdict={'size':16,"color":"b"});
绘制目标图像
#绘制目标图像
# 准备数据
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/mtcars.csv") #导入数据集
x = df.loc[:, ['mpg']] #提取目标数据
df['mpg_z'] = (x - x.mean())/x.std() #对目标数据进行z-score标准化处理
df['colors'] = ['red' if x < 0 else 'green' for x in df['mpg_z']] #生成颜色标签列
df.sort_values('mpg_z', inplace=True) #根据标准化后的数据对整个数据集进行排序
df.reset_index(inplace=True,drop=True) #重置数据集的索引
#保留两位小数
round(df.mpg_z.values[0],2)
#创建画布
plt.figure(figsize=(12,10),dpi=65)
#绘制基础的发散型条形图
plt.hlines(y=df.cars,xmin=0,xmax=df.mpg_z)
#添加文本
for i in range(df.shape[0]):
x = df.mpg_z[i]
y = df.index[i]
s = str(round(df.mpg_z[i],2))
plt.text(x,y,s,color=df.colors[i])
这个基础图形有3个问题:
- 文本信息都显示在线的上方,而我们需要的是显示在线条正对着的位置(中间)
- 红色文本显示在线条的右边,我们需要的是横坐标小于0的显示在左边,大于0的显示在右边
- 文本字体有点小
#修正文本信息
plt.figure(figsize=(14,10),dpi=65)
#绘制基础图形
plt.hlines(y=df.cars,xmin=0,xmax=df.mpg_z)
#添加文本
for i in range(df.shape[0]):
x = df.mpg_z[i]
y = df.index[i]
s = str(round(df.mpg_z[i],2))
plt.text(x,y,s
,fontdict={'size':12,'color':df.colors[i]}
,horizontalalignment='right' if x<0 else 'left'
,verticalalignment='center'
)
#添加装饰
plt.yticks(fontsize=12)#y轴刻度标签,并设置文字大小
plt.title('Diverging text Bars of Car Mileage', fontdict={'size':20}) #设置图形标题
plt.xlim(-2.5,2.5)
plt.grid(linestyle='-.',alpha=0.5);
这里需要说明的是:
- 1、水平对其参数,用来类似列表推导式的代码,不同的是这里只需要单个值不需要最后形成列表(所以在最外层没有套上列表符号[])
- 2、另外:left和right这两个对其方式也很容易混淆
- 3、以右对齐为例来说明:
首先我们需要知道的是文本放置的位置其实就是线条的末端,所谓的右对齐就是要求文本的最右端要与这个位置对其。
发散型包点图
发散型包点图与发散型条形图,发散型文本都非常相似,可以说这个发散型包点图包含了条形图和文本图的两部分信息。
- 横坐标:里程
- 纵坐标:汽车名称
- 颜色:大于0显示绿色,小于0显示红色
1、导入需要的包
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
2、认识绘制包点图的函数
前面我们有说过,包点图其实与发散条形图和发散文本图非常相似,此外包点图也是散点图的一种变形。
从我们需要绘制的目标函数可以看出来,包点图中包含了散点和文本,所以,我们绘制的包点图就是由散点图和文本图两部分组成的。
plt.scatter
重要参数:
x,y:绘制散点图的数据
s:散点的大小
c:散点的颜色
marker:散点的形状(默认是圆形)
plt.text()
重要参数:
x,y:文本放置的位置坐标
s:文本的内容
horizontalalignment:水平对齐控制参数(center/left/right三种可选)
verticalalignment:垂直对齐控制参数(‘top’, ‘bottom’, ‘center’, ‘baseline’, 'center_baseline’五种可选)
#绘制简单的散点图和文本图
import numpy as np
data = np.random.randn(10)
data
import matplotlib.pyplot as plt
plt.scatter(range(10),data,s=500,c='r',alpha=0.3);
#绘制10个位置的文本图
plt.figure(figsize=(8,5))
plt.scatter(range(10),data,s=500,c="b",alpha=0.5)
for x, y in zip(range(10),data):
plt.text(x,y,round(y,1),fontdict={'color':'w','size':10},
horizontalalignment='center',
verticalalignment='center'
);
#认识绘图数据
import pandas as pd
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/mtcars.csv")
#提取出目标特征
x = df.loc[:,['mpg']] #对于提取单列数据,如果在列名外加[]就会生成dataframe格式的数据,如果不加则生成series
#对数据进行z-score标准化处理,将生成的标准化数据增加到df中
df['mpg_z'] = (x - x.mean())/x.std()
#生成颜色标签
df['colors'] = ['green' if x>0 else 'red' for x in df['mpg_z']]
#根据标准化之后的数据,对整个数据集进行排序
df.sort_values('mpg_z',inplace=True)
#重置整个数据集的索引
#df.reset_index(inplace=True)
df.index = range(df.shape[0])
#绘制发散型包点图
plt.figure(figsize=(12,15),dpi=60)
#绘制基础散点图
plt.scatter(df.mpg_z,df.index,s=500,alpha=0.6,color=df.colors)
#添加文本
for x, y, text in zip(df.mpg_z,df.index,df.mpg_z):
plt.text(x,y,round(text,1),
fontdict={"color":'w',"size":10}
,horizontalalignment='center'
,verticalalignment='center')
#添加装饰
plt.title('Diverging Dotplot of Car Mileage',fontsize=20)
plt.xlabel('$mileage$',fontsize=12)
plt.yticks(df.index,df.cars,fontsize=12)
plt.xlim(-2.5,2.5)
plt.grid(linestyle='-.',alpha=0.3)
发散型包点图到这里基本上就已经完成了!
面积图
面积图就是通过轴和线之间的区域进行着色形成的一种图形,它不仅可以强调峰值和低谷,还可以强调高点和低点的持续时间,持续时间越长,面积就越大。
- 横坐标:时间(月份)
- 纵坐标:月度收益率
- 颜色:>0显示绿色,<0显示红色
- 注释:收益率最高的点
1、导入需要的绘图库
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
#如果是jupyter notebook则需要运行这句代码,把生成的png图片嵌入到cell中
#如果是jupyter lab则可以不用运行这句代码,它会自动将生成图片嵌入到cell中
2、认识绘制面积图的函数
plt.fill_between
函数功能:填充两条水平曲线之间的区域
重要参数说明:
x:定义曲线的x坐标
y1:定义第一条曲线的y坐标
y2:定义第二条曲线的y坐标
where:定义被填充的区域
interpolate:控制交叉点位置的填充(仅在设定where并且两条曲线相交时起作用)
3、简单绘图
#绘制简单的面积天冲突
x = np.random.randn(1000)
x.sort()
y1 = np.sin(x)
y2 = np.cos(x)
#设置两个画布,画两张图
#绘制简单的线型图
plt.figure(figsize=(8,5),dpi=80)
plt.plot(x,y1)
plt.plot(x,y2);
#填充两条曲线之间的区域
plt.figure(figsize=(8,5),dpi=80)
plt.fill_between(x,y1,y2,color="g",alpha=0.5);
从图中可以发现:两条曲线之间的区域全部被填充为一种颜色。我们需要在增加一定的难度:在y1>y2时,填充为红色,否则,填充为蓝色。
plt.fill_between(x,y1,y2,where=y1>y2,color='r',alpha=0.5,interpolate=True)
plt.fill_between(x,y1,y2,where=y1<y2,color='b',alpha=0.5,interpolate=True);
#注意:interpolate=True表示将计算实际交叉点并将填充区域扩展到此点
4、绘制目标图像
#认识我们的绘图数据集
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/economics.csv",parse_dates=['date'])
#parse_dates的作用是将字符串序列转换为日期序列
#将字符串文本转换为日期型文本
df1 = pd.read_csv("https://github.com/selva86/datasets/raw/master/economics.csv")
#转换
df1["date1"] = pd.to_datetime(df1.date)
#方法2.使用datetime.datetime.strptime()
import datetime
df1["date2"]= df1["date"].apply(lambda x:datetime.datetime.strptime(x,"%Y-%m-%d"))
#df1['date2'] = df1['date'].apply(lambda x:datetime.datetime.strptime(x, "%Y-%m-%d"))
准备绘图所需数据集:绘制目标图像,我们需要的是月度收益率,纵观所有特征,与月度收益率相关的特征是个人储蓄率(psavert),而且数据比较对,因此在这里选取前100来绘图。
#提取目标特征
df.psavert.values
plt.plot(df.psavert.values);
df.psavert.diff()
#计算收益
income = df.psavert.diff().fillna(0)
#计算月度收益率(收益/本金 = (本身-前)/前)
re = (income/df.psavert.shift(1)).fillna(0)*100
#创建画布并设定画布尺寸
plt.figure(figsize=(14,8),dpi=60)
#绘制面积图
plt.fill_between(range(df.shape[0]),re,0,where=re>0,color='g',interpolate=True, alpha=0.7)
plt.fill_between(range(df.shape[0]),re,0,where=re<=0,color='r',interpolate=True, alpha=0.7);
#提取年份
y = df.date.dt.year
#提取月份,并且显示英文名字
m = df.date.dt.month_name()
#将年份和月份打包
[*zip(y,m)]
str(m[1])[:3].upper()
#将月份和年份组合成‘JUL-1967’形式
[str(m)[:3].upper()+'-'+str(y) for y,m in zip(y,m)][::6]
#创建画布并设定画布尺寸
plt.figure(figsize=(14,8),dpi=60)
#绘制面积图
plt.fill_between(range(df.shape[0]),re,0,where=re>0,color='g',interpolate=True, alpha=0.7)
plt.fill_between(range(df.shape[0]),re,0,where=re<=0,color='r',interpolate=True, alpha=0.7)
#修改横坐标刻度
xtickslabel = [str(m)[:3].upper()+'-'+str(y) for y,m in zip(y,m)][::6]
plt.xticks(range(df.shape[0])[::6],xtickslabel,rotation=90,fontsize=12)
#添加装饰
plt.title('Month Economics Return %',fontsize=20)
plt.ylabel('Monthly returns %',fontsize=15)
plt.ylim(-35,35)
plt.xlim(1,100)
plt.grid(alpha=.3);