> 文章列表 > 笔记本 - 数据分析百宝箱

笔记本 - 数据分析百宝箱

笔记本 - 数据分析百宝箱

Numpy

一、基本操作:

属性:

improt numpy as np

生成数组:

array=np.array([[1,2,3],[2,3,4]],dtype=np.int/float)

array.npim: 几维的数组

array.shape: 几行几列;

array.size : 数组内几个元素

a=np.zeros/ones( (3,4) ) 生成一个全部是1的三行四列的矩阵

a=np.arange(10,20,2) 生成一个10到20,步长为2的数组

a=np.arrange(12).reshape((3,4)) 重新定义shape

a=linspace(1,10,5)生成一个从1到10的线段

a=np.array([10,20,30]); b=np.arrange(4); c=a+b ;print© 可以进行数学基本运算

print(b<3)将数组内数字进行判断,返回ture或flase

乘法:c=a*b(逐个相乘) c_dot=np.dot(a,b) ==c_dot=a.dot(b):矩阵相乘

生成随机的矩阵:

a=np.random.randint((2,4))

数组操作:

reshape(a, newshape[, order]) 在不更改数据的情况下为数组赋予新的形状。
ravel(a[, order]) 返回一个连续的扁平数组。
ndarray.flat 数组上的一维迭代器。
tile(A, reps) 通过重复A代表次数来构造一个数组。
delete(arr, obj[, axis]) 返回一个新的数组,该数组具有沿删除的轴的子数组。
unique(ar[, return_index, return_inverse, …]) 查找数组的唯一元素
resize(a, new_shape) 返回具有指定形状的新数组。
删除举例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KCm0cOpV-1681776922723)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1643893309568.png)]

重复举例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z18qJ2hR-1681776922725)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1643893602277.png)]

数组形状:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XA9uqZVm-1681776922726)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1643893869571.png)]

过滤掉零:
a = np.array((0, 0, 0, 1, 2, 3, 0, 2, 1, 0))
>>> np.trim_zeros(a)
array([1, 2, 3, 0, 2, 1])

数学方法:

先生成一个随机的数组:

a=np.random.randint((2,4))

方法名 解释:
np.sum(a,axis=1) 在第一行寻求总和(最大、最小)
np.argmin(a) 寻找最小最大的索引
np.mean(a) 求平均值
np.cumsum(a) 累加
np.concatenate((arr,arr),axis=0/1) 数组拼接,按行或者按列
np.sin()/cos()/tan() 求正弦余弦正切值
np.amin()和np.amax() 指定轴的最大最小值
np.ptp() 数组元素的最大值最小值的差
np.median() 数组中的中位数
np.std() 数组元素的标准差
np.var() 方差
np.nonzero(a) 输出a数组中非0的数的索引
np.sort(a) 从小到大逐行进行排序
np.transpose(a) 矩阵转置
np.clip(a,最小值,最大值) 按照你给定的最小值、最大值进行数组截取

索引:

A=np.arrange(3,15).reshape((3,4))

print(A[1] [1]):输出第一行第一列,一维数组就是是直接索引;

print(A[2,1])输出第二行第一列的一个数

还可以运用切片:A[ : , 1]:每行的第一列数

for column in A.T:print(A.T)  #转置后输出行,也就是按列输出
for item in A.flat:print(item)		#将数组里每一个数字单个输出

合并和分割:

合并:

A=np.array([1,1,1]) ; B=np.arrray([2,2,2])

print(np.vstack((A,B))) :上下合并,变成两行

print**(np.hstack((A,B)**)) :左右合并,就一行

A[ : , np.newaxis] :纵向合并,一行三列分成三行一列

np.concatenate( (A,B,B,A) ,axis=0) :纵向和横向合并

分割:

A=np.arrange(12).reshape((3,4))

np.split(A,2,axis=0) : axis=0就是指定的行 axis=1是指定的列

split只能等量分割,用 np.array_split(A,3,axis=1)是不等量分割

np.vsplit( (A,3) ) 横向分割,hsplit是纵向分割

Numpy的常用函数

where:

where是一种条件函数,可以指定满足条件与不满足条件位置对应的填充值:

a = np.array([-1,1,-1,0])
np.where(a>0, a, 5) # 对应位置为True时填充a对应元素,否则填充5

nonzero, argmax, argmin

这三个函数返回的都是索引,nonzero返回非零数的索引,argmax, argmin分别返回最大和最小数的索引:

a = np.array([-2,-5,0,1,3,-1])np.nonzero(a)
a.argmax()
a.argmin()

any, all

any`指当序列至少存在一`True`或非零元素时返回`True`,否则返回`False
all`指当序列元素全为 `True`或非零元素时返回`True`,否则返回`False

累乘累加

cumprod, cumsum分别表示累乘和累加函数,返回同长度的数组,diff表示数组中的每一个元素和前一个元素做差,由于第一个元素为缺失值,因此在默认参数情况下,返回长度是原数组减1

a = np.array([1,2,3])
a.cumprod()
a.cumsum()
np.diff(a)

含缺失值的计算

因为数组里面含有缺失值,所以使用函数返回的也是缺失值,所以我们要过滤掉这些缺失值

target = np.array([1, 2, np.nan])
np.nanmax(target)  #过滤掉nan计算最大值
np.nanquantile(target, 0.5)

矩阵计算

向量内积:

a = np.array([1,2,3])
b = np.array([1,3,5])
a.dot(b)

向量范数和矩阵范数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IyxuQiL1-1681776922727)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647175424022.png)]

matrix_target =  np.arange(4).reshape(-1,2)np.linalg.norm(matrix_target, 'fro')
np.linalg.norm(matrix_target, np.inf)

Pandas:

提取原数据:用 .arrayto_numpy 提取数据

排除缺失值:一般的聚合函数都有 skipna 关键字,指定是否要排除缺失数据,默认值为 True

  • 注意:NumPy 的 meanstdsum 等方法默认不统计 Series 里的空值

常用方法:

函数 描述
count 统计非空值数量
sum 汇总值
mean 平均值
mad 平均绝对偏差
median 算数中位数
min 最小值
max 最大值
mode 众数
abs 绝对值
prod 乘积
std 贝塞尔校正的样本标准偏差
var 无偏方差
sem 平均值的标准误差
skew 样本偏度 (第三阶)
kurt 样本峰度 (第四阶)
quantile 样本分位数 (不同 % 的值)
cumsum 累加
cumprod 累乘
cummax 累积最大值
cummin 累积最小值

最大/小位置索引:

Series 与 DataFrame 的 idxmax()idxmin() 函数计算最大值与最小值对应的索引。

idxminidxmax 对应 NumPy 里的 argminargmax

apply()函数:

apply()方法沿着 DataFrame 的轴应用函数,比如,描述性统计方法,该方法支持 axis 参数。

它就是对DataFrame的行或列进行操作

In [141]: df.apply(np.mean)
Out[141]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64In [142]: df.apply(np.mean, axis=1)
Out[142]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64In [143]: df.apply(lambda x: x.max() - x.min())
Out[143]: 
one      1.051928
two      1.632779
three    1.840607
dtype: float64

apply() 方法还支持通过函数名字符串调用函数。

In [146]: df.apply('mean')
Out[146]: 
one      0.811094
two      1.360588
three    0.187958
dtype: float64In [147]: df.apply('mean', axis=1)
Out[147]: 
a    1.583749
b    0.734929
c    1.133683
d   -0.166914
dtype: float64

聚合API

就是组合多个函数

tsdf.agg(['sum', 'mean'])
Out[160]: A         B         C
sum   3.033606 -1.803879  1.575510
mean  0.505601 -0.300647  0.262585In [161]: tsdf.A.agg(['sum', 'mean'])
Out[161]: 
sum     3.033606
mean    0.505601
Name: A, dtype: float64

用字典实现聚合

指定为哪些列应用哪些聚合函数时,需要把包含列名与标量(或标量列表)的字典传递给 DataFrame.agg

注意:这里输出结果的顺序不是固定的,要想让输出顺序与输入顺序一致,请使用 OrderedDict

In [165]: tsdf.agg({'A': 'mean', 'B': 'sum'})
Out[165]: 
A    0.505601
B   -1.803879
dtype: float64

输入的参数是列表时,输出结果为 DataFrame,并以矩阵形式显示所有聚合函数的计算结果,且输出结果由所有唯一函数组成。未执行聚合操作的列输出结果为 NaN 值:

In [166]: tsdf.agg({'A': ['mean', 'min'], 'B': 'sum'})
Out[166]: A         B
mean  0.505601       NaN
min  -0.749892       NaN
sum        NaN -1.803879

transform函数

.transform() 支持 NumPy 函数、字符串函数及自定义函数。

tsdf.transform(np.abs)/tsdf.transform('abs')/tsdf.transform(lambda x: x.abs())

函数字典可以为每列执行指定 transform() 操作。

tsdf.transform({'A': np.abs, 'B': lambda x: x + 1})valus值可以使用列表

Series

Series是一维的,显示列索引,Series(data=[],index=[])

import pandas as pd
from pandas import Seriess= Series(data=[1,2,3,'four'],index=['a','b','c','d'])
dic={'语文':120,'数学':117,'英语':121,'理综':224,
}
s= Series(data=dic)		#数据源可以是numpy的矩阵、数组也可以是字典
s[0:2]			#切片操作和numpy一样head(),tail():首尾5个数据
unique():去重

s=dp.Series([1,3,6,np.nan,44,1]) #pandas会自动给列表内元素加上编号,一个一行输出

pandas就是把列表内数据进行行和列的排序,行和列的名字可以直接定义并输出

DataFrame:

DataFrame是二维的,自动生成行列的索引

from pandas import DataFrame
df=DataFrame(data=np.random.randint(10,20,size=(4,4)),index=['a','b','c','d']dic={'name':['zhangsan','liis','wangwu'],'salary':[1000,2000,3000]
}
df=DataFrame(data=dic)DataFrame的属性:values(返回整个数据的数组),columns(行索引),index(列索引),shape()

dates=pd.date_range(‘20160101’,perides=6) 从1号一直输出到6号,因为给perides=6

df=pd.DataFrame(np.random.randn(6,4),index=dates,columns=[‘a’,‘b’,‘c’,‘d’])

获取属性

我们可以打印

Series: dtype, index,values,name, shape,

DataFrame: dtypes,index , columns,values,shape , .T

(类型,行索引、列索引、值,长度)

describe()/ info(): 获取数据信息

读写文件

header=None表示第一行不作为列名,index_col表示把某一列或几列作为索引,索引的内容将会在第三章进行详述,usecols表示读取列的集合,默认读取所有的列,parse_dates表示需要转化为时间的列,关于时间序列的有关内容将在第十章讲解,nrows表示读取的数据行数。上面这些参数在上述的三个函数里都可以使用。

pd.read_csv('../data/my_csv.csv', index_col=['col1', 'col2']) 可指定多列为索引列pd.read_csv('../data/my_csv.csv', parse_dates=['col5']) 指定时间列-----
在读取txt文件时,经常遇到分隔符非空格的情况,read_table有一个分割参数sep,它使得用户可以自定义分割符号,进行txt数据的读取。例如,下面的读取的表以||||为分割:上面的结果显然不是理想的,这时可以使用sep,同时需要指定引擎为python:pd.read_table('../data/my_table_special_sep.txt', sep=' \\|\\|\\|\\| ', engine='python')

特征统计函数:

需要介绍的是quantile, count, idxmax这三个函数,它们分别返回的是分位数、非缺失值个数、最大值对应的索引:

df_demo.quantile(0.75)
df_demo.count()
df_demo.idxmax() # idxmin是对应的函数

唯一值:

对序列使用uniquenunique可以分别得到其唯一值组成的列表和唯一值的个数:

df['School'].unique()df['School'].nunique()

去重的使用:

duplicateddrop_duplicates的功能类似,但前者返回了是否为唯一值的布尔列表,其keep参数与后者一致。其返回的序列,把重复元素设为True,否则为Falsedrop_duplicates等价于把duplicatedTrue的对应行剔除。

df_demo.drop_duplicates(['Name', 'Gender'], keep=False).head() # 保留只出现过一次的性别和姓名组合

替换函数:

替换操作是针对某一个列进行的,因此下面的例子都以Series举例。pandas中的替换函数可以归纳为三类:映射替换、逻辑替换、数值替换。其中映射替换包含replace方法、第八章中的str.replace方法以及第九章中的cat.codes方法,此处介绍replace的用法。

在replace中, 可以通过字典构造,或者传入两个列表来进行替换:

df['Gender'].replace({'Female':0, 'Male':1}).head()
# 把女换成0,男换成1
df['Gender'].replace(['Female', 'Male'], [0,1]).head()
#两种都可以

还可以用最前一个或者后一个的值进行替换:

指定method参数为ffill则为用前面一个最近的未被替换的值进行替换,bfill则使用后面最近的未被替换的值进行替换

s = pd.Series(['a', 1, 'b', 2, 1, 1, 'a'])
s.replace([1, 2], method='ffill')
s.replace([1, 2], method='bfill') #替换1,2

逻辑替换:

包括了wheremask,这两个函数是完全对称的:where函数在传入条件为False的对应行进行替换,而mask在传入条件为True的对应行进行替换,当不指定替换值时,替换为缺失值。

s = pd.Series([-1, 1.2345, 100, -50])
s.where(s<0, 100)
s.mask(s<0, -50)  #符合条件的用,对应的值进行替换

排序:

索引排序的用法和值排序完全一致,只不过元素的值在索引中,此时需要指定索引层的名字或者层号,用参数level表示。另外,需要注意的是字符串的排列顺序由字母顺序决定。

set_index:指定多列可生成多级索引的表
demo = df[['Grade', 'Name', 'Height', 'Weight']].set_index(['Grade','Name'])df.sort_index(level['Grade','Name'],axis=0,ascending=Flase):对行进行排序df.sort_values(by=[‘E’]):对E这一列的值进行排序

多列排序:

在排序中,经常遇到多列排序的问题,比如在体重相同的情况下,对身高进行排序,并且保持身高降序排列,体重升序排列:

df_demo.sort_values(['Weight','Height'],ascending=[True,False]).head()

选择排序:

列索引:

df[‘A’] :选择输出A这一列
df[['A','B']] : 输出多列放在列表中
等价于
df.A
在df.query里面 直接使用列名 带空格用英文 的这个符号`...`

行索引

1.字符串索引
s = pd.Series([1, 2, 3, 4, 5, 6], index=['a', 'b', 'a', 'a', 'a', 'c'])s['a'] 和s[['a','b']]索引切片:(注意索引不能重复)
s['c': 'b': -2]如果索引重复:需要排序后切片
s.sort_index()['a': 'b']2. 整数索引
s[1]  s[[1,2,3]]如果使用整数切片,则会取出对应索引位置的值,注意这里的整数切片同Python中的切片一样不包含右端点:s[1:-1:2] 步长为2

print(df[0:3],df[‘20130102’:‘20130104’] :用切片或者指定区间

loc是标签选择

loc索引器的一般形式是loc[*, *],其中第一个*代表行的选择,第二个*代表列的选择,如果省略第二个位置写作loc[*],这个*是指行的筛选。其中,*的位置一共有五类合法对象,分别是:单个元素、元素列表、元素切片、布尔列表以及函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pnXStrJb-1681776922732)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647179615440.png)]

df.loc[20130102]df.loc[ : ,[‘A’,‘B’]:打印所有行和A和B两列df.loc[20130102, [ ‘A’ , ‘B’] ] :输出20130102这行的A和B两列的值【b】*为元素列表
此时,取出列表中所有元素值对应的行或列:
df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]【c】*为切片之前的Series使用字符串索引时提到,如果是唯一值的起点和终点字符,那么就可以使用切片,并且包含两个端点,如果不唯一则报错:df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']【d】*为布尔列表在实际的数据处理中,根据条件来筛选行是极其常见的,此处传入loc的布尔列表与DataFrame长度相同,且列表为True的位置所对应的行会被选中,False则会被剔除。例如,选出体重超过70kg的学生:
df_demo.loc[df_demo.Weight>70].head()也可以通过isin方法返回的布尔列表等价写出,例如选出所有大一和大四的同学信息:
df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head()isnotin???? 不不不,这里是使用bool类型,前面加逻辑非运算就好了
df_demo.loc[~df_demo.Grade.isin(['Freshman', 'Senior'])].head()组合条件查询:
#组合1
condition_1_1 = df_demo.School == 'Fudan University'
condition_1_2 = df_demo.Grade == 'Senior'
condition_1_3 = df_demo.Weight > 70condition_1 = condition_1_1 & condition_1_2 & condition_1_3
#组合2
condition_2_1 = df_demo.School == 'Peking University'
condition_2_2 = df_demo.Grade == 'Senior'
condition_2_3 = df_demo.Weight > 80condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3df_demo.loc[condition_1 | condition_2]将条件查询封装成函数
def condition(x):condition_1_1 = x.School == 'Fudan University'condition_1_2 = x.Grade == 'Senior'condition_1_3 = x.Weight > 70condition_1 = condition_1_1 & condition_1_2 & condition_1_3condition_2_1 = x.School == 'Peking University'condition_2_2 = x.Grade == 'Senior'condition_2_3 = x.Weight > 80condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3result = condition_1 | condition_2return resultdf_demo.loc[condition]lambda形式:
df_demo.loc[lambda x:'Quan Zhao', lambda x:'Gender']由于函数无法返回如start: end: step的切片形式,故返回切片时要用slice对象进行包装:
df_demo.loc[lambda x: slice('Gaojuan You', 'Gaoqiang Qian')]
iloc是位置选择

df.iloc[3,1] :选择第三行第一列

df.iloc[ [3:5 , 1:3] :三到五行的一到三列

di.iloc[[1,3,5],1:3] :选择1,3,5行的第一到第三列

布尔选择:

在使用布尔列表的时候要特别注意,不能传入Series,而必须传入序列的values,否则会报错。因此,在使用布尔筛选的时候还是应当优先考虑loc的方式。

df_demo.iloc[(df_demo.Weight>80).values].head()

ix是标签和位置一起使用

df.ix[ :3 , [ ‘A’ , ‘C’ ]] :第0行到第3行,A和C两列

是否筛选:

print(df[df.A>8]) :在A这列筛选值大于8的,A列输出满足条件的行时,同行的数据也会输出

选定位置后赋值:

df.iloc[2,2]=1111 df.iloc[‘20100101’ , ‘B’]=2222 横向和纵向定位更改

df[df.A>0]=0 A这列大于0的数字变成0

df[‘F’]=np.nan 加上F这列,赋值nan

df[‘E’]=pd.Series( [1,2,3,4,5,6] ,index=pd.date_range(‘20130101’ , periods=6) )

缺失值统计

缺失数据可以使用isnaisnull(两个函数没有区别)来查看每个单元格是否缺失,结合mean可以计算出每列缺失值的比例:

df.isnull()/isna()

如果想要查看某一列缺失或者非缺失的行,可以利用Series上的isna或者notna进行布尔索引。

例如,查看身高缺失的行:

df[df.Height.isna()]

如果想要同时对几个列,检索出全部为缺失或者至少有一个缺失或者没有缺失的行,可以使用isna, notnaany, all的组合。例如,对身高、体重和转系情况这3列分别进行这三种情况的检索:

sub_set = df[['Height', 'Weight', 'Transfer']]
df[sub_set.isna().all(1)] # 全部缺失
df[sub_set.isna().any(1)].head() # 至少有一个缺失
df[sub_set.notna().all(1)].head() # 没有缺失

dropna

dropna的主要参数为轴方向axis(默认为0,即删除行)、删除方式how、删除的非缺失值个数阈值thresh(非缺失值非缺失值没有达到这个数量的相应维度会被删除)、备选的删除子集subset,其中how主要有anyall两种参数可以选择。

例如,删除身高体重至少有一个缺失的行:
res = df.dropna(how = 'any', subset = ['Height', 'Weight'])
res.shape
例如,删除超过15个缺失值的列:
res = df.dropna(1, thresh=df.shape[0]-15) # 身高被删除
res.head()

fillna

fillna中有三个参数是常用的:value, method, limit。其中,value为填充值,可以是标量,也可以是索引到元素的字典映射;

method为填充方法,有用前面的元素填充ffill和用后面的元素填充bfill两种类型,

limit参数表示连续缺失值的最大填充次数。

s.fillna(method='ffill') # 用前面的值向后填充
s.fillna(method='ffill', limit=1) # 连续出现的缺失,最多填充一次
s.fillna(s.mean()) # value为标量
s.fillna({'a': 100, 'd': 200}) # 通过索引映射填充的值有时为了更加合理地填充,需要先进行分组后再操作。例如,根据年级进行身高的均值填充:
df.groupby('Grade')['Height'].transform(lambda x: x.fillna(x.mean())).head()

插值函数

线性插值、最近邻插值和索引插值

对于interpolate而言,除了插值方法(默认为linear线性插值)之外,有与fillna类似的两个常用参数,

一个是控制方向的limit_direction

另一个是控制最大连续缺失值插值个数的limit

其中,限制插值的方向默认为forward,这与fillnamethod中的ffill是类似的,若想要后向限制插值或者双向限制插值可以指定为backwardboth

默认线性插值法下分别进行backward和双向限制插值,同时限制最大连续条数为1:
res = s.interpolate(limit_direction='backward/both', limit=1)
res.values第二种常见的插值是最近邻插补,即缺失值的元素和离它最近的非缺失值元素一样:
s.interpolate('nearest').values最后来介绍索引插值,即根据索引大小进行线性插值。例如,构造不等间距的索引进行演示:s.interpolate() # 默认的线性插值,等价于计算中点的值,1和10中间缺失补5
s.interpolate(method='index') 
# 和索引有关的线性插值,计算相应索引大小对应的值,1和10中间缺失补1,因为差10个索引,每个索引为1 ,即索引线性对于时间戳亦可用

python中的缺失值用None表示,该元素除了等于自己本身之外,与其他任何元素不相等:

numpy中利用np.nan来表示缺失值,该元素除了不和其他任何元素相等之外,和自身的比较结果也返回False

在时间序列的对象中,pandas利用pd.NaT来指代缺失值,它的作用和np.nan是一致的

由于np.nan的浮点性质,如果在一个整数的Series中出现缺失,那么其类型会转变为float64;而如果在一个布尔类型的序列中出现缺失,那么其类型就会转为object而不是bool

Nullable类型的性质

从字面意义上看Nullable就是可空的,言下之意就是序列类型不受缺失值的影响。例如,在上述三个Nullable类型中存储缺失值,都会转为pandas内置的pd.NA

一般在实际数据处理时,可以在数据集读入后,先通过convert_dtypes转为Nullable类型:

df = pd.read_csv('../data/learn_pandas.csv')
df = df.convert_dtypes()
df.dtypes

缺失数据的计算:

  1. 当调用函数sum, prod使用加法和乘法的时候,缺失数据等价于被分别视作0和1,即不改变原来的计算结果:

  2. 当使用累计函数时,会自动跳过缺失值所处的位置:

  3. 另外需要注意的是,diff, pct_change这两个函数虽然功能相似,但是对于缺失的处理不同,前者凡是参与缺失计算的部分全部设为了缺失值,而后者缺失值位置会被设为 0% 的变化率

删除行列:

#删除B和C两列,两种方法
df.drop(['B', 'C'], axis=1)
df.drop(columns=['B', 'C'])#删除行
df.drop([0])
df.drop([0, 1])#删除(del、pop)列的方式也与字典类似
del df['two']three = df.pop('three')

query方法

pandas中,支持把字符串形式的查询表达式传入query方法来查询数据,其表达式的执行结果必须返回布尔列表。在进行复杂索引时,由于这种检索方式无需像普通方法一样重复使用DataFrame的名字来引用列名,一般而言会使代码长度在不降低可读性的前提下有所减少。

df.query('((School == "Fudan University")&'' (Grade == "Senior")&'' (Weight > 70))|''((School == "Peking University")&'' (Grade != "Senior")&'' (Weight > 80))')

query表达式中,帮用户注册了所有来自DataFrame的列名,所有属于该Series的方法都可以被调用,和正常的函数调用并没有区别,例如查询体重超过均值的学生:

df.query('Weight > Weight.mean()').head() 

对于含有空格的列名,需要使用英文顿号col name的方式进行引用

同时,在query中还注册了若干英语的字面用法,帮助提高可读性,例如:or, and, or, in, not in。例如,筛选出男生中不是大一大二的学生:

df.query('(Grade not in ["Freshman", "Sophomore"]) and (Gender == "Male")').head()

此外,在字符串中出现与列表的比较时,==!=分别表示元素出现在列表和没有出现在列表,等价于innot in,例如查询所有大三和大四的学生:

df.query('Grade == ["Junior", "Senior"]').head()

引入外部变量:

对于query中的字符串,如果要引用外部变量,只需在变量名前加@符号。例如,取出体重位于70kg到80kg之间的学生:

low, high =70, 80
df.query('Weight.between(@low, @high)').head()

索引案例

df['Cocoa Percent'] = df['Cocoa Percent'].apply(lambda x:float(x[:-1])/100)
df.query('(Rating<3)&(`Cocoa Percent`>`Cocoa Percent`.median())')df.loc[lambda x:x.Rating<=2.75&(x["Cocoa Percent"]>x["Cocoa Percent"].median())]
idx = pd.IndexSlice
exclude = ['France', 'Canada', 'Amsterdam', 'Belgium']
res = df.set_index(['Review Date', 'Company Location']).sort_index(level=0)#这里对索引再就行筛选
res.loc[idx[2012:,~res.index.get_level_values(1).isin(exclude)],:].head(3)

随机抽样:

如果把DataFrame的每一行看作一个样本,或把每一列看作一个特征,再把整个DataFrame看作总体,想要对样本或特征进行随机抽样就可以用sample函数。有时在拿到大型数据集后,想要对统计特征进行计算来了解数据的大致分布,但是这很费时间。同时,由于许多统计特征在等概率不放回的简单随机抽样条件下,是总体统计特征的无偏估计,比如样本均值和总体均值,那么就可以先从整张表中抽出一部分来做近似估计


sample函数中的主要参数为n, axis, frac, replace, weights,前三个分别是指抽样数量、抽样的方向(0为行、1为列)和抽样比例(0.3则为从总体中抽出30%的样本)。

replaceweights分别是指是否放回和每个样本的抽样相对概率,当replace = True则表示有放回抽样。例如,对下面构造的df_samplevalue值的相对大小为抽样概率进行有放回抽样,抽样数量为3。

df_sample.sample(3, replace = True, weights = df_sample.value)

多级索引

loc:

由于多级索引中的单个元素以元组为单位,因此之前在第一节介绍的 lociloc 方法完全可以照搬,只需把标量的位置替换成对应的元组。

当传入元组列表或单个元组或返回前二者的函数时,需要先进行索引排序以避免性能警告:

df_sorted = df_multi.sort_index()
df_sorted.loc[('Fudan University', 'Junior')].head()

获取索引:

df.index/columns.names/values
df.index.get_level_values(0/1) 获取第一层、第二层但对于索引而言,无论是单层还是多层,用户都无法通过index_obj[0] = item的方式来修改元素,也不能通过index_name[0] = new_name的方式来修改名字经典案例:
idx = pd.IndexSlice
exclude = ['France', 'Canada', 'Amsterdam', 'Belgium']
res = df.set_index(['Review Date', 'Company Location']).sort_index(level=0)res.loc[idx[2012:,~res.index.get_level_values(1).isin(exclude)],:].head(3)

当使用切片时需要注意,在单级索引中只要切片端点元素是唯一的,那么就可以进行切片,但在多级索引中,无论元组在索引中是否重复出现,都必须经过排序才能使用切片,否则报错

#报错
df_multi.loc[('Fudan University', 'Senior'):].head()
df_unique.loc[('Fudan University', 'Senior'):].head()
#更正
df_unique.sort_index().loc[('Fudan University', 'Senior'):].head()

在多级索引中的元组有一种特殊的用法,可以对多层的元素进行交叉组合后索引,但同时需要指定loc的列,全选则用:表示。其中,每一层需要选中的元素用列表存放,传入loc的形式为[(level_0_list, level_1_list), cols]。例如,想要得到所有北大和复旦的大二大三学生,可以如下写出:

res = df_multi.loc[(['Peking University', 'Fudan University'], ['Sophomore', 'Junior']), :]
res.head()

下面的语句和上面类似,但仍然传入的是元素(这里为元组)的列表,它们的意义是不同的,表示的是选出北大的大三学生和复旦的大二学生:

res = df_multi.loc[[('Peking University', 'Junior'), ('Fudan University', 'Sophomore')]]
res.head()

想获取查询的子表的长度时,别用len了!!

result.shape[0] / shape[1]

indexSlice对象

前面介绍的方法,即使在索引不重复的时候,也只能对元组整体进行切片,而不能对每层进行切片,也不允许将切片和布尔列表混合使用,引入IndexSlice对象就能解决这个问题。Slice对象一共有两种形式,第一种为loc[idx[*,*]]型,第二种为loc[idx[*,*],idx[*,*]]

为了使用silce对象,先要进行定义:idx = pd.IndexSlice
loc[idx[*,*]]型这种情况并不能进行多层分别切片,前一个*表示行的选择,后一个*表示列的选择,与单纯的loc是类似的:df_ex.loc[idx['C':, ('D', 'f'):]]
df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0------loc[idx[*,*],idx[*,*]]型这种情况能够分层进行切片,前一个idx指代的是行索引,后一个是列索引。
df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
但需要注意的是,此时不支持使用函数
例如:
df_ex.loc[idx[:'A', lambda x: 'b'], idx['E':, 'e':]]

多级索引构造

前面提到了多级索引表的结构和切片,那么除了使用set_index之外,如何自己构造多级索引呢?常用的有from_tuples, from_arrays, from_product三种方法,它们都是pd.MultiIndex对象下的函数。

from_tuples指根据传入由元组组成的列表进行构造:

my_tuple = [('a','cat'),('a','dog'),('b','cat'),('b','dog')]
pd.MultiIndex.from_tuples(my_tuple, names=['First','Second'])

from_arrays指根据传入列表中,对应层的列表进行构造:

my_array = [list('aabb'), ['cat', 'dog']*2]
pd.MultiIndex.from_arrays(my_array, names=['First','Second'])

from_product指根据给定多个列表的笛卡尔积进行构造:

my_list1 = ['a','b']
my_list2 = ['cat','dog']
pd.MultiIndex.from_product([my_list1, my_list2], names=['First','Second'])

构造举例:

p.random.seed(0)
L1,L2,L3 = ['A','B'],['a','b'],['alpha','beta']
mul_index1 = pd.MultiIndex.from_product([L1,L2,L3], names=('Upper', 'Lower','Extra'))
L4,L5,L6 = ['C','D'],['c','d'],['cat','dog']
mul_index2 = pd.MultiIndex.from_product([L4,L5,L6], names=('Big', 'Small', 'Other'))
df_ex = pd.DataFrame(np.random.randint(-9,10,(8,8)), index=mul_index1,  columns=mul_index2)
df_ex

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-riUavTPe-1681776922733)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647218897760.png)]

交换:

索引层的交换由swaplevelreorder_levels完成,前者只能交换两个层,而后者可以交换任意层,两者都可以指定交换的是轴是哪一个,即行索引或列索引:

df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一层和第三层交换df_ex.reorder_levels([2,0,1],axis=0).head() 
# 列表数字指代原来索引中的层
把原来第3层换到第1层,原来第1层换到第2层,原来第2层换到第3层

删除某一层:

df_ex.droplevel([0,1],axis=0/1)  #0是行索引,1是列索引

修改:

通过rename_axis可以对索引层的名字进行修改,常用的修改方式是传入字典的映射:

df_ex.rename_axis(index={'Upper':'Changed_row'}, columns={'Other':'Changed_Col'}).head()通过rename可以对索引的值进行修改,如果是多级索引需要指定修改的层号level:
df_ex.rename(columns={'cat':'not_cat'}, level=2).head()df_ex.rename(index=lambda x:str.upper(x), level=2).head()另外一个需要介绍的函数是map,它是定义在Index上的方法,与前面rename方法中层的函数式用法是类似的,只不过它传入的不是层的标量值,而是直接传入索引的元组,这为用户进行跨层的修改提供了遍历
df_temp = df_ex.copy()
new_idx = df_temp.index.map(
lambda x: (x[0], x[1], str.upper(x[2])))
df_temp.index = new_idx
df_temp.head()

索引的设置与重置

索引的设置可以使用set_index完成,这里的主要参数是append,表示是否来保留原来的索引,直接把新设定的添加到原索引的内层:

df_new.set_index(['A','B'])

reset_indexset_index的逆函数,其主要参数是drop,表示是否要把去掉的索引层丢弃,而不是添加到列中:

df_new.reset_index(['D'],drop=False)

更改行列标签名字:

1.df.columns 查看当前行标签df.indexs  = []df.columns = []新标签列表
2.DataFrame.rename(index={},columns={'原标签名':'新标签名'})df.rename(columns={"A":"a"})df.rename(index={0:'A'})
3.df.reindex(index=[...],columns=[...])	

多重索引:

df = pd.DataFrame({'x': [1, 2, 3, 4, 5, 6],'y': [10, 20, 30, 40, 50, 60]},index=pd.MultiIndex.from_product([['a', 'b', 'c'], [1, 2]],names=['let', 'num']))In [242]: df
Out[242]: x   y
let num       
a   1    1  102    2  20
b   1    3  302    4  40
c   1    5  502    6  60

数据删除和填充:

NAN可以参与运算,None不能参与运算

all:用来检测行或者列中是否存在True

df.isnull().any(axis=1)

df.dropna(axis=0,how=‘any’)

在行中有任何一个none就就掉,如果how=all的话,一行所有的都是none就丢掉

df.fillna(value=填入值) 把已有的none自动填入数据

nf.fillna(method=‘ffill/bfill’,axis=0/1),向前/后填充,水平/上下

df.isnull() 缺失返回True,否则Flase,永远结合any,只要有True就返回True

df.notnull():缺失返回Flase,否则True,永远结合all,只要有True就返回True

读表格:pd.read_excel/csv(‘文件名+后缀’)

改行索引:

df.set_index(‘date’,inplace=True)

删除重复:

nf.drop_duplicates(keep = ‘first/last’)

级联

pd.concat(df1,df2,axis=1/0)合并:
pd.merge(df1,df2,on='根据哪一列合并,即合并条件,合并的列都要有是共同的')merge(df1,df2,how='inner/outner/right/left')

.dt 访问器

Series 提供一个可以简单、快捷地返回 datetime 属性值的访问器。这个访问器返回的也是 Series,索引与现有的 Series 一样。

s = pd.Series(pd.date_range('20130101 09:10:12', periods=4))
s.dt.hour
s.dt.second
s.dt.day筛选:
s[s.dt.day == 2]

日期处理

还可以用 [Series.dt.strftime()datetime 的值当成字符串进行格式化,支持与标准 [strftime() 同样的格式。

s.dt.strftime('%Y/%m/%d')df_dt.apply(lambda x: datetime.strftime(x, format))
或
df_dt.dt.strftime(format)#将int转换成str
df_date = df['日期'].apply(str)
#用to_datetime()函数将字符串转换成时间格式,并增加'时间'字段
df['时间'] = pd.to_datetime(df_date,format='%Y/%m/%d')
print(df['时间'])
#将日期格式化,并增加'格式化日期'字段
df['格式化日期1'] = df.时间.apply(lambda x: datetime.strftime(x, format='%Y-%m-%d'))
df['格式化日期2'] = df.时间.dt.strftime('%Y-%m-%d')
print(df['格式化日期1'],'\\n',df['格式化日期2'])
#抽取'时间'字段中的值
df['时间.年'] = df['时间'].dt.year
df['时间.月'] = df['时间'].dt.month
df['时间.周'] = df['时间'].dt.weekday
df['时间.日'] = df['时间'].dt.day
df['时间.时'] = df['时间'].dt.hour
df['时间.分'] = df['时间'].dt.minute
df['时间.秒'] = df['时间'].dt.second

修改指定类型:

#法一:
dft = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]})dft[['a', 'b']] = dft[['a', 'b']].astype(np.uint8)#法二:
dft1 = dft1.astype({'a': np.bool, 'c': np.float64})

Pandas 提供了多种函数可以把 object 从一种类型强制转为另一种类型。这是因为,数据有时存储的是正确类型,但在保存时却存成了 object 类型,此时,用 DataFrame.infer_objects()Series.infer_objects() 方法即可把数据转换为正确的类型。

df.infer_objects().dtypes

下列函数可以应用于一维数组与标量,执行硬转换,把对象转换为指定类型。

  • to_numeric() ,转换为数值型
In [370]: m = ['1.1', 2, 3]In [371]: pd.to_numeric(m)
Out[371]: array([1.1, 2. , 3. ])
  • to_datetime() (opens new window),转换为 datetime 对象
In [372]: import datetimeIn [373]: m = ['2016-07-09', datetime.datetime(2016, 3, 2)]In [374]: pd.to_datetime(m)
Out[374]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)

分组(Grouping)

groupby后会生成一个groupby对象,该对象不会返回任何内容

从上述的几个例子中不难看出,想要实现分组操作,必须明确三个要素:

分组依据、数据来源、操作及其返回结果。

同时从充分性的角度来说,如果明确了这三方面,就能确定一个分组操作,从而分组代码的一般模式即:

df.groupby(分组依据)[数据来源].使用操作

例如第一个例子中的代码就应该如下:

df.groupby('Gender')['Longevity'].mean()

基本内容:

  1. 根据某一列分组
  2. 根据某几列分组
  3. 组容量与组数
  4. 组的遍历
  5. level参数(多级索引)和axis参数

分组完想df可视化 : to_frame()

“group by” 指的是涵盖下列一项或多项步骤的处理流程:

  • 分割:按条件把数据分割成多组;
  • 应用:为每组单独应用函数;
  • 组合:将处理结果组合成一个数据结构
分组后取出一个分组:
df_group = df.groupby('...')
df_group.get_group(分组名)根据几组分:df.groupby(['...'])查看组的容量: group.size  返回的是表长乘以表宽的大小,但在groupby对象上表示统计每个组的元素个数查看组数:group.ngroupsgroups属性是获取分组的字典:gb.groups.item()/keys()分完组然后选中列:df.groupby('...')[['col1,col2']].mean()

条件分组:

condition = df.Weight > df.Weight.mean()
df.groupby(condition)['Height'].mean()根据上下四分位数分割,将体重分为high、normal、low三组,统计身高的均值
def split_weight(x):if x>df.Weight.quantile(0.75):return 'high'elif x<df.Weight.quantile(0.25):return 'low'else:return 'normal'
df.groupby(split_weight)['Height'].mean()
 df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar','foo', 'bar', 'foo', 'foo'],'B': ['one', 'one', 'two', 'three',                          'two', 'two', 'one', 'three'],'C': np.random.randn(8),'D': np.random.randn(8)})df.groupby('A').sum()
df.groupby(['A', 'B']).sum()通过get_group方法可以直接获取所在组对应的行,此时必须知道组的具体名字:
gb.get_group(('Fudan University', 'Freshman'))连续变量分组:
bins = [0,40,60,80,100]
cuts = pd.cut(df['Math'],bins=bin)
df.groupby(cuts)['Math'].count()同时使用多个聚合函数:
group_m.agg('sum','mean','std')group_m.agg({'Math':['sum','mean','std'],'Height':'var'})group_m['Math'].agg(lambda x: x.max()-min())

1. 内置聚合函数

在介绍agg之前,首先要了解一些直接定义在groupby对象的聚合函数,因为它的速度基本都会经过内部的优化,使用功能时应当优先考虑。根据返回标量值的原则,包括如下函数:max/min/mean/median/count/all/any/idxmax/idxmin/mad/nunique/skew/quantile/sum/std/var/sem/size/prod

这些聚合函数当传入的数据来源包含多个列时,将按照列进行迭代计算:gb = df.groupby('Gender')[['Height', 'Weight']]
gb.max()

agg方法

虽然在groupby对象上定义了许多方便的函数,但仍然有以下不便之处:

  • 无法同时使用多个函数
  • 无法对特定的列使用特定的聚合函数
  • 无法使用自定义的聚合函数
  • 无法直接对结果的列名在聚合前进行自定义命名

问题解决方法:

【a】使用多个函数当使用多个聚合函数时,需要用列表的形式把内置聚合函数对应的字符串传入,先前提到的所有字符串都是合法的。
gb.agg(['sum', 'idxmax', 'skew'])【b】对特定的列使用特定的聚合函数
对于方法和列的特殊对应,可以通过构造字典传入agg中实现,其中字典以列名为键,以聚合字符串或字符串列表为值。
gb.agg({'Height':['mean','max'], 'Weight':'count'})【c】使用自定义函数在agg中可以使用具体的自定义函数, 需要注意传入函数的参数是之前数据源中的列,逐列进行计算 。
gb.agg(lambda x: x.mean()-x.min())由于传入的是序列,因此序列上的方法和属性都是可以在函数中使用的,只需保证返回值是标量即可。
下面的例子是指,如果组的指标均值,超过该指标的总体均值,返回High,否则返回Low。def my_func(s):res = 'High'if s.mean() <= df[s.name].mean():res = 'Low'return res
gb.agg(my_func)【d】聚合结果重命名如果想要对聚合结果的列名进行重命名,只需要将上述函数的位置改写成元组,元组的第一个元素为新的名字,第二个位置为原来的函数,包括聚合字符串和自定义函数,现举若干例子说明:
gb.agg([('range', lambda x: x.max()-x.min()), ('my_sum', 'sum')])gb.agg({'Height': [('my_func', my_func), 'sum'], 'Weight': lambda x:x.max()})另外需要注意,使用对一个或者多个列使用单个聚合的时候,重命名需要加方括号,否则就不知道是新的名字还是手误输错的内置函数字符串:
gb.agg([('my_sum', 'sum')])

三、变换和过滤

变换函数的返回值为同长度的序列,最常用的内置变换函数是累计函数:cumcount/cumsum/cumprod/cummax/cummin,它们的使用方式和聚合函数类似,只不过完成的是组内累计操作。

当用自定义变换时需要使用transform方法,被调用的自定义函数,其传入值为数据源的序列其传入值为数据源的序列,与agg的传入类型是一致的,其最后的返回结果是行列索引与数据源一致的DataFrame

gb.transform(lambda x: (x-x.mean())/x.std()).head()

组过滤作为行过滤的推广,指的是如果对一个组的全体所在行进行统计的结果返回True则会被保留,False则该组会被过滤,最后把所有未被过滤的组其对应的所在行拼接起来作为DataFrame返回。

groupby对象中,定义了filter方法进行组的筛选,其中自定义函数的输入参数为数据源构成的DataFrame本身,在之前例子中定义的groupby对象中,传入的就是df[['Height', 'Weight']],因此所有表方法和属性都可以在自定义函数中相应地使用,同时只需保证自定义函数的返回为布尔值即可。

在原表中通过过滤得到所有容量大于100的组:
gb.filter(lambda x: x.shape[0] > 100).head()

分组apply用法:

def BMI(x):Height = x['Height']/100Weight = x['Weight']BMI_value = Weight/Height**2return BMI_value.mean()
gb.apply(BMI)

分组案例实战

先过滤出所属Country数超过2个的汽车,即若该汽车的Country在总体数据集中出现次数不超过2则剔除,再按Country分组计算价格均值、价格变异系数、该Country的汽车数量,其中变异系数的计算方法是标准差除以均值,并在结果中把变异系数重命名为CoV。
按照表中位置的前三分之一、中间三分之一和后三分之一分组,统计Price的均值。
对类型Type分组,对Price和HP分别计算最大值和最小值,结果会产生多级索引,请用下划线把多级列索引合并为单层索引。
对类型Type分组,对HP进行组内的min-max归一化。
对类型Type分组,计算Disp.与HP的相关系数。df.groupby('Country').filter(lambda x:x.shape[0]>2)
.groupby('Country')['Price']
.agg([('CoV', lambda x: x.std()/x.mean()), 'mean', 'count'])#表的长度为60
condition = ['Head']*20+['Mid']*20+['Tail']*20df.groupby(condition)['Price'].mean()res = df.groupby('Type').agg({'Price': ['max'], 'HP': ['min']})
# res.columns = res.columns.map(lambda x:'_'.join(x))
res.columnsres = df.groupby('Type')['Price','HP'].agg(['max','min'])
# res.columns = res,columns.map(lambda x:'_'.join(x))
res

Apply函数用法

apply函数会遍历指定的所有行或者列

  1. 标量返回

    df[['school','Math','Height']].groupby('school').apply(lambda x:x.max())
    
  2. 列表返回

    df[['school','Math','Height']].groupby('school')
    .apply(lambda x: pd.DataFrame({"col":values}))
    
  3. 数据框返回

criterion = ver['applicant_race_name_1'].map(lambda x: x.startswith('W'))df2['..'] = df2['...'].map(str.strip)
df2.columns = df2.columns.str.upper
df['...'].apply(str.upper)

自定义函数:

自定义函数的接收值就是前面指定的行/列的值,经过处理后,return的值就是填充进去的

def my_mean(x):res = x.mean()return res
df_demo.apply(my_mean)

窗口对象

pandas中有3类窗口,分别是滑动窗口rolling、扩张窗口expanding以及指数加权窗口ewm。需要说明的是,以日期偏置为窗口大小的滑动窗口将在第十章讨论,指数加权窗口见本章练习。

1. 滑窗对象

要使用滑窗函数,就必须先要对一个序列使用.rolling得到滑窗对象,其最重要的参数为窗口大小window

s = pd.Series([1,2,3,4,5])
roller = s.rolling(window = 3)  #三个数据为一组的滑窗
roller在得到了滑窗对象后,能够使用相应的聚合函数进行计算,需要注意的是窗口包含当前行所在的元素,例如在第四个位置进行均值运算时,应当计算(2+3+4)/3,而不是(1+2+3)/3:
roller.mean()
roller.sum()  #前面几个和后面几个会出现NAN值滑动系数和协方差矩阵
roller.cov(s2)
roller.corr(s2)支持传入自定义函数:
roller.apply(lambda x:x.mean())

滑窗函数:

shift, diff, pct_change是一组类滑窗函数,它们的公共参数为periods=n,默认为1

shift表示取向前第n个元素的值

diff与向前第n个元素做差(与Numpy中不同,后者表示n阶差分)

pct_change与向前第n个元素相比计算增长率。这里的n可以为负,表示反方向的类似操作。

s = pd.Series([1,3,6,10,15])
s.shift(2)
s.diff(3)可用apply函数等价替换
s.rolling(3).apply(lambda x:list(x)[0]) # s.shift(2)
s.rolling(4).apply(lambda x:list(x)[-1]-list(x)[0]) # s.diff(3)

扩张窗口:

扩张窗口又称累计窗口,可以理解为一个动态长度的窗口,其窗口的大小就是从序列开始处到具体操作的对应位置,其使用的聚合函数会作用于这些逐步扩张的窗口上。具体地说,设序列为a1, a2, a3, a4,则其每个位置对应的窗口即[a1]、[a1, a2]、[a1, a2, a3]、[a1, a2, a3, a4]。

s = pd.Series([1, 3, 6, 10])
s.expanding().mean()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j2OPrmxR-1681776922734)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647178551663.png)]

Ex2:指数加权窗口

  1. 作为扩张窗口的ewm窗口

在扩张窗口中,用户可以使用各类函数进行历史的累计指标统计,但这些内置的统计函数往往把窗口中的所有元素赋予了同样的权重。事实上,可以给出不同的权重来赋给窗口中的元素,指数加权窗口就是这样一种特殊的扩张窗口。

其中,最重要的参数是alpha,它决定了默认情况下的窗口权重为𝑤𝑖=(1−𝛼)𝑖,𝑖∈{0,1,…,𝑡}wi=(1−α)i,i∈{0,1,…,t},其中𝑖=𝑡i=t表示当前元素,𝑖=0i=0表示序列的第一个元素。

从权重公式可以看出,离开当前值越远则权重越小,若记原序列为x,更新后的当前元素为y_t,此时通过加权公式归一化后可知:

$$
\\begin{split}y_t &=\\frac{\\sum_{i=0}^{t} w_i x_{t-i}}{\\sum_{i=0}^{t} w_i} \\
&=\\frac{x_t + (1 - \\alpha)x_{t-1} + (1 - \\alpha)^2 x_{t-2} + …

  • (1 - \\alpha)^{t} x_{0}}{1 + (1 - \\alpha) + (1 - \\alpha)^2 + …
  • (1 - \\alpha)^{t}}\\\\end{split}
    $$

对于Series而言,可以用ewm对象如下计算指数平滑后的序列:

np.random.seed(0)
s = pd.Series(np.random.randint(-1,2,30).cumsum())
s.head()s.ewm(alpha=0.2).mean().head()

作为滑动窗口的ewm窗口

从第1问中可以看到,ewm作为一种扩张窗口的特例,只能从序列的第一个元素开始加权。现在希望给定一个限制窗口n,只对包含自身的最近的n个元素作为窗口进行滑动加权平滑。请根据滑窗函数,给出新的wiyt的更新公式,并通过rolling窗口实现这一功能。

变形

一、长宽表的变形

什么是长表?什么是宽表?这个概念是对于某一个特征而言的。例如:

一个表中把性别存储在某一个列中,那么它就是关于性别的长表;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BuX6hVsh-1681776922736)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647336037964.png)]

如果把性别作为列名,列中的元素是某一其他的相关特征数值,那么这个表是关于性别的宽表。下面的两张表就分别是关于性别的长表和宽表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AKKQesgJ-1681776922737)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647336049468.png)]

pivot

pviot表具有三个参数,index,columns,values, pivot相关的三个参数允许被设置为列表,这也意味着会返回多级索引 .

利用pivot进行变形操作需要满足唯一性的要求,即由于在新表中的行列索引对应了唯一的value,因此原表中的indexcolumns对应两个列的行组合必须唯一。

将此表进行变形:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AmdAfn6T-1681776922739)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647336658156.png)]

df.pivot(index='Name', columns='Subject', values='Grade')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V6I1kIR6-1681776922740)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647336686939.png)]

多级索引变形:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o0uZDEUH-1681776922740)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647336827247.png)]

pivot_multi = df.pivot(index = ['Class', 'Name'],columns = ['Subject','Examination'], #将这两列变为列索引values = ['Grade','rank'])
pivot_multi

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hWJgCVH3-1681776922741)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647336867260.png)]

pivot_table

如果我们想在pivot表的基础上进行统计计算或边缘合并,就得用到pivot_table

其中的aggfunc参数就是使用的聚合函数进行统计计算。上述场景可以如下写出:

df.pivot_table(index = 'Name',columns = 'Subject',values = 'Grade',aggfunc = lambda x:x.mean())

边缘合并:

可以通过设置margins=True来实现,其中边际的聚合方式与aggfunc中给出的聚合方法一致

df.pivot_table(index = 'Name',columns = 'Subject',values = 'Grade',aggfunc='mean',margins=True)

melt

melt表就是和pivot表的互逆转化

df_melted = df.melt(id_vars = ['Class', 'Name'], #行索引value_vars = ['Chinese', 'Math'],	# 将这俩个列索引变成列的值var_name = 'Subject',		# 上面变成列的列名字value_name = 'Grade')		#变化后的列索引名
df_melted

wide_to_long

现在如果列中包含了交叉类别,比如期中期末的类别和语文数学的类别,那么想要把values_name对应的Grade扩充为两列分别对应语文分数和数学分数,只把期中期末的信息压缩,这种需求下就要使用wide_to_long函数来完成。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfIIbBUr-1681776922743)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647337487641.png)]

就是说 列名中含有交叉组合的元素,我们可以把相同的部分提取出来变成新的两列,不同的部分拿出来变成索引

分割符后面的公共部分列名会提出来,分隔符前面为新增列名并保存分隔符后面的列的值

#加一个前缀名,即获得一个新的列,就能把要提取出来的列的值放进去

pd.wide_to_long(df,stubnames=['Chinese', 'Math'],新增的两列i = ['Class', 'Name'], 索引j='Examination', 新增的索引sep='_',  #列名分割符suffix='.+')                

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bc9uoQdg-1681776922747)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647337629715.png)]

索引的变形

swaplevel或者reorder_levels进行索引内部的层交换,下面就要讨论行列索引之间行列索引之间的交换,由于这种交换带来了DataFrame维度上的变化,因此属于变形操作。在第一节中提到的4种变形函数与其不同之处在于,它们都属于某一列或几列元素元素和列索引列索引之间的转换,而不是索引之间的转换。

#构造
df = pd.DataFrame(np.ones((4,2)),index = pd.Index([('A', 'cat', 'big'),('A', 'dog', 'small'),('B', 'cat', 'big'),('B', 'dog', 'small')]),columns=['col_1', 'col_2'])unstack的主要参数是移动的层号,默认转化最内层,移动到列索引的最内层,同时支持同时转化多个层:
df.unstack(0/1/2)
df.unstack([0,2])

unstack相反,stack的作用就是把列索引的层压入行索引,其用法完全类似

df.stack(0/1/2)

变换完之后,记得reset_index(), 如果想改变列的位置,sort_index([‘往后移的列(想往前移的列的前面的列’])

3. get_dummies

get_dummies是用于特征构建的重要函数之一,其作用是把类别特征转为指示变量。例如,对年级一列转为指示变量,属于某一个年级的对应列标记为1,否则为0:

pd.get_dummies(df.Grade).head()

连接

merge函数基于 on=' ',how=' ', on是指定根据哪一个公共列连接,how是根据df1,还是df2 , 左右其实就是代码书写的顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-okHRppfy-1681776922748)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647346037176.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QTFgKoIA-1681776922749)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647346026597.png)]


如果两个表中想要连接的列不具备相同的列名,可以通过left_onright_on指定:

df1 = pd.DataFrame({'df1_name':['San Zhang','Si Li'], 'Age':[20,30]})
df2 = pd.DataFrame({'df2_name':['Si Li','Wu Wang'], 'Gender':['F','M']})
df1.merge(df2, left_on='df1_name', right_on='df2_name', how='left')

如果两个表中的列出现了重复的列名,那么可以通过suffixes参数指定。例如合并考试成绩的时候,第一个表记录了语文成绩,第二个是数学成绩:

df1 = pd.DataFrame({'Name':['San Zhang'],'Grade':[70]})
df2 = pd.DataFrame({'Name':['San Zhang'],'Grade':[80]})
df1.merge(df2, on='Name', how='left', suffixes=['_Chinese','_Math'])

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-thVerRQB-1681776922750)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647346225585.png)]

在某些时候出现重复元素是麻烦的

例如两位同学来自不同的班级,但是姓名相同,这种时候就要指定on参数为多个列使得正确连接:

df1.merge(df2, on='Name', how='left') # 错误的结果
df1.merge(df2, on=['Name', 'Class'], how='left') # 正确的结果

从上面的例子来看,在进行基于唯一性的连接下,如果键不是唯一的,那么结果就会产生问题。举例中的行数很少,但如果实际数据中有几十万到上百万行的进行合并时,如果想要保证唯一性,除了用duplicated检查是否重复外,merge中也提供了validate参数来检查连接的唯一性模式。这里共有三种模式,

即一对一连接1:1,一对多连接1:m,多对一连接m:1连接,第一个是指左右表的键都是唯一的,后面两个分别指左表键唯一和右表键唯一。

直接用 validate=‘1:m’

索引连接

所谓索引连接,就是把索引当作键,因此这和值连接本质上没有区别,pandas中利用join函数来处理索引连接,它的参数选择要少于merge,除了必须的onhow之外,

可以对重复的列指定左右后缀lsuffixrsuffix

其中,on参数指索引名,单层索引时省略参数表示按照当前索引连接。

df1.join(df2, how='left')
df1.join(df2, how='left', lsuffix='_Chinese', rsuffix='_Math')

方向连接

有时候用户并不关心以哪一列为键来合并,只是希望把两个表或者多个表按照纵向或者横向拼接,为这种需求,pandas中提供了concat函数来实现。

concat中,最常用的有三个参数,它们是axis, join, keys,分别表示拼接方向,连接形式,以及在新表中指示来自于哪一张旧表的名字。这里需要特别注意,joinkeys与之前提到的join函数和键的概念没有任何关系。

在默认状态下的axis=0,表示纵向拼接多个表,常常用于多个样本的拼接;

axis=1表示横向拼接多个表,常用于多个字段或特征的拼接


虽然说concat是处理关系型合并的函数,但是它仍然是关于索引进行连接的。纵向拼接会根据列索引对齐,

默认状态下join=outer表示保留所有的列,并将不存在的值设为缺失;

join=inner表示保留两个表都出现过的列。横向拼接则根据行索引对齐,join参数可以类似设置。

**注意!!!,当确认要使用多表直接的方向合并时,尤其是横向的合并,可以先用reset_index方法恢复默认整数索引再进行合并,防止出现由索引的误对齐和重复索引的笛卡尔积带来的错误结果。 **

最后,keys参数的使用场景在于多个表合并后,用户仍然想要知道新表中的数据来自于哪个原表,这时可以通过keys参数产生多级索引进行标记。例如,第一个表中都是一班的同学,而第二个表中都是二班的同学,可以使用如下方式合并:

df1 = pd.DataFrame({'Name':['San Zhang','Si Li'], 'Age':[20,21]})
df2 = pd.DataFrame({'Name':['Wu Wang'],'Age':[21]})
pd.concat([df1, df2], keys=['one', 'two'])

序列与表的合并

利用concat可以实现多个表之间的方向拼接,如果想要把一个序列追加到表的行末或者列末,则可以分别使用appendassign方法。

append中,如果原表是默认整数序列的索引,那么可以使用ignore_index=True对新序列对应的索引自动标号,否则必须对Series指定name属性。

s = pd.Series(['Wu Wang', 21], index = df1.columns)
df1.append(s,ignore_index=True)

对于assign而言,虽然可以利用其添加新的列,但一般通过df['new_col'] = ...的形式就可以等价地添加新列。同时,使用[]修改的缺点是它会直接在原表上进行改动,而assign返回的是一个临时副本:

s = pd.Series([80, 90])
df1.assign(Grade=s)

表格错位问题

注意最后一列是"0:00:00"还是"00:00:00",加了用户id,时间在第5列最优解: df.fillna(df['00:00:00'],inplace)  用最后一列的非空值去填补0:00:00的空值def reset_table(df_,length):ddf= df_.iloc[length:,:]del ddf["00"]ddf.insert(5,"0:00",ddf["00:00:00"])del ddf["00:00:00"]ddf.rename(columns={"0:00":"0:00:00"},inplace=True)rdf = df_.iloc[:length-1,:]del rdf["00:00:00"]rdf.rename(columns={"00":"0:00:00"},inplace=True)df_1 = pd.concat([ddf,rdf],axis=0)return df_1
col_name=df3.columns.tolist()
col_name[5] ="00" 
df3.columns = col_name
reset_table(df3,14525)
-----
merge = pd.concat([df,df1,df2,df3,df4,df5],axis=0)
col_name=merge.columns.tolist()
col_name[4] ="00" 
merge.columns = col_name
df7=reset_table(merge,len(df))-----
def get_userid(df):j=0i=0while(i!=len(df)):df.iloc[i,0] = get_user_id(df.iloc[i,2])j=j+1i=i+1return dfdef get_user_id(v):for i,j in name_dict.items():if j==v:return i

文本数据

一、str对象

1. str对象的设计意图

str对象是定义在IndexSeries上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其str对象。

在Python标准库中也有str模块,为了使用上的便利,有许多函数的用法pandas照搬了它的设计,例如字母转为大写的操作:

var = 'abcd'
str.upper(var) # Python内置str模块s.str.upper() # pandas中str对象上的upper方法对于str对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过[]可以取出某个位置的元素:
var[0]
var[-1: 0: -2]
s.str[0]

最后需要注意的是,对于全体元素为数值类型的序列,即使其类型为object或者category也不允许直接使用str属性。如果需要把数字当成string类型处理,可以使用astype强制转换为string类型的Series

s = pd.Series([12, 345, 6789])
s.astype('string').str[1]

文本处理的五类操作

1. 拆分

str.split能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数n,是否展开为多个列expand

s = pd.Series(['上海市黄浦区方浜中路249号', '上海市宝山区密山路5号'])
s.str.split('[市区路]')
s.str.split('[市区路]', n=2, expand=True)
不拆成列,就返回列表 ; 拆成列就一个值一个列

2. 合并

关于合并一共有两个函数,分别是str.joinstr.cat

str.join表示用某个连接符把Series中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值:

#值由元素列表构成
s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
s.str.join('-')

str.cat用于合并两个序列,主要参数为连接符sep、连接形式join以及缺失值替代符号na_rep,其中连接形式默认为以索引为键的左连接。

s1 = pd.Series(['a','b'])
s2 = pd.Series(['cat','dog'])
s1.str.cat(s2,sep='-')s2.index = [1, 2]
s1.str.cat(s2, sep='-', na_rep='?', join='outer')

3. 匹配

str.contains返回了每个字符串是否包含正则模式的布尔序列

s = pd.Series(['my cat', 'he is fat', 'railway station'])
s.str.contains('\\s\\wat')s.str.startswith('my')s.str.match('m|h')

4. 替换

str.replacereplace并不是一个函数,在使用字符串替换时应当使用前者。

s = pd.Series(['a_1_b','c_?'])
s.str.replace('\\d|\\?', 'new', regex=True)

5. 提取

提取既可以认为是一种返回具体元素(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用str.extract进行提取:

pat = '(\\w+市)(\\w+区)(\\w+路)(\\d+号)'
s.str.extract(pat)

通过子组的命名,可以直接对新生成DataFrame的列命名:

pat = '(?P<市名>\\w+市)(?P<区名>\\w+区)(?P<路名>\\w+路)(?P<编号>\\d+号)'
s.str.extract(pat)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mCoP3M2h-1681776922752)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647418651419.png)]

str.extractall不同于str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:

s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
pat = '[A|B](\\d+)[T|S](\\d+)'
s.str.extractall(pat)pat_with_name = '[A|B](?P<name1>\\d+)[T|S](?P<name2>\\d+)'
s.str.extractall(pat_with_name)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8UQO0jts-1681776922754)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647418836432.png)]

str对象函数:

upper, lower, title, capitalize, swapcase , strip, rstrip, lstrip

s.str.upper()

数值型

这里着重需要介绍的是pd.to_numeric方法,它虽然不是str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。

其主要参数包括errorsdowncast分别代表了非数值的处理模式和转换类型。

其中,对于不能转换为数值的有三种errors选项,

raise, coerce, ignore分别表示直接报错、设为缺失以及保持原来的字符串。

在数据清洗时,可以利用coerce的设定,快速查看非数值型的行:

s[pd.to_numeric(s, errors='coerce').isna()]

统计型函数

countlen的作用分别是返回出现正则模式的次数和字符串的长度:

s = pd.Series(['cat rat fat at', 'get feed sheet heat'])
s.str.count('[r|f]at|ee')
s.str.len()

对于填充型函数而言,pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:

s = pd.Series(['a','b','c'])
s.str.pad(5,'left/right/both','*')

分类数据

一、cat对象

1. cat对象的属性

pandas中提供了category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用astype方法。

s = df.Grade.astype('category')
s.head()

在一个分类类型的Series中定义了cat对象,它和上一章中介绍的str对象类似,定义了一些属性和方法来进行分类类别的操作。

对于一个具体的分类,有两个组成部分,其一为类别的本身,它以Index类型存储,其二为是否有序,它们都可以通过cat的属性被访问:

s.cat.categories
s.cat.ordered另外,每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于cat.categories中的顺序,该属性可以通过codes访问:
s.cat.codes.head()

2. 类别的增加、删除和修改

通过cat对象的categories属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?

【NOTE】类别不得直接修改

在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandascat 属性上定义了若干方法来达到相同的目的。

s = s.cat.add_categories('Graduate') # 增加一个毕业生类别
s = s.cat.remove_categories('Freshman')
s = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别s = s.cat.rename_categories({'Sophomore':'本科二年级学生'})

有序分类

1. 序的建立

有序类别和无序类别可以通过as_unorderedreorder_categories互相转化,需要注意的是后者传入的参数必须是由当前序列的无序类别构成的列表,不能够增加新的类别,也不能缺少原来的类别,并且必须指定参数ordered=True,否则方法无效。

例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:

s = df.Grade.astype('category')
s = s.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)s.cat.as_unordered().head()

【NOTE】类别不得直接修改

如果不想指定ordered=True参数,那么可以先用s.cat.as_ordered()转化为有序类别,再利用reorder_categories进行具体的相对大小调整。

2. 排序和比较

在第二章中,曾提到了字符串和数值类型序列的排序,此时就要说明分类变量的排序:只需把列的类型修改为category后,再赋予相应的大小关系,就能正常地使用sort_indexsort_values

例如,对年级进行排序:

df.Grade = df.Grade.astype('category')
df.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)df.sort_values('Grade').head() # 值排序
df.set_index('Grade').sort_index().head() # 索引排序

由于序的建立,因此就可以进行比较操作。分类变量的比较操作分为两类,第一种是==!=关系的比较,比较的对象可以是标量或者同长度的Series(或list),第二种是>,>=,<,<=四类大小关系的比较,比较的对象和第一种类似,但是所有参与比较的元素必须属于原序列的categories,同时要和原序列具有相同的索引

res1 = df.Grade == 'Sophomore'
res3 = df.Grade <= 'Sophomore'
res4 = df.Grade <= df.Grade.sample(frac=1).reset_index(drop=True) # 打乱后比较
res4.head()

1. 利用cut和qcut进行区间构造

区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过cutqcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。

首先介绍cut的常见用法:

其中,最重要的参数是bins,如果传入整数n,则代表把整个传入数组的按照最大和最小值等间距地分为n段。由于区间默认是左开右闭,需要在调整时把最小值包含进去,在pandas中的解决方案是在值最小的区间左端点再减去0.001*(max-min),因此如果对序列[1,2]划分为2个箱子时,第一个箱子的范围(0.999,1.5],第二个箱子的范围是(1.5,2]。如果需要指定左闭右开时,需要把right参数设置为False,相应的区间调整方法是在值最大的区间右端点再加上0.001*(max-min)

s = pd.Series([1,2])
pd.cut(s, bins=2)
pd.cut(s, bins=2, right=False)
#指定分割点
pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])

另外两个常用参数为labelsretbins,分别代表了区间的名字和是否返回分割点(默认不返回):

s = pd.Series([1,2])
res = pd.cut(s, bins=2, labels=['small', 'big'], retbins=True)
res[0] #分割标签
res[1] #分割点

从用法上来说,qcutcut几乎没有差别,只是把bins参数变成的q参数,qcut中的q是指quantile。这里的q为整数n时,指按照n等分位数把数据分箱,还可以传入浮点列表指代相应的分位数分割点。

s = df.Weight
pd.qcut(s, q=3).head()
pd.qcut(s, q=[0,0.2,0.8,1]).head()

2. 一般区间的构造

对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态,其中开闭状态可以指定right, left, both, neither中的一类:

my_interval = pd.Interval(0, 1, 'right')
my_interval

其属性包含了mid, length, right, left, closed,,分别表示中点、长度、右端点、左端点和开闭状态

  • 使用in可以判断元素是否属于区间

  • 使用overlaps可以判断两个区间是否有交集:

0.5 in my_intervalmy_interval_2 = pd.Interval(0.5, 1.5, 'left')
my_interval.overlaps(my_interval_2)

一般而言,pd.IntervalIndex对象有四类方法生成,分别是from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:

  • from_breaks的功能类似于cutqcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:
pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
  • from_arrays是分别传入左端点和右端点的列表,适用于有交集并且知道起点和终点的情况:
pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')
  • from_tuples传入的是起点和终点元组构成的列表:
pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')
  • 一个等差的区间序列由起点、终点、区间个数和区间长度决定,其中三个量确定的情况下,剩下一个量就确定了,interval_range中的start, end, periods, freq参数就对应了这四个量,从而就能构造出相应的区间:
pd.interval_range(start=1,end=5,periods=8)pd.interval_range(end=5,periods=8,freq=0.5)

除此之外,如果直接使用pd.IntervalIndex([...], closed=...),把Interval类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的closed类型,因为pd.IntervalIndex只允许存放同一种开闭区间的Interval对象。

pd.IntervalIndex([my_interval, my_interval_2], closed='left')

3. 区间的属性与方法

IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用cut或者qcut的结果进行分析,那么需要先将其转为该种索引类型:

id_interval = pd.IntervalIndex(pd.cut(s, 3))
id_interval[:3]与单个Interval类型相似,IntervalIndex有若干常用属性:
left, right, mid, length
分别表示左右端点、两端点均值和区间长度。id_demo.left/right/mid/lengthIntervalIndex还有两个常用方法,包括contains和overlaps,分别指逐个判断每个区间是否有交集
id_demo.contains(4)
id_demo.overlaps(pd.Interval(40,60))

钻石数据集分类

  1. 分别对df.cutobject类型和category类型下使用nunique函数,并比较它们的性能。
  2. 钻石的切割质量可以分为五个等级,由次到好分别是Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。
  3. 分别采用两种不同的方法,把cut, clarity这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
  4. 对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的category序列依次添加到原表中。
  5. 第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
  6. 对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。
df = pd.read_csv('../data/diamonds.csv') 
df.cut = df.cut.astype('category').cat.reorder_categories(['Fair', 'Good', 'Very Good', 'Premium', 'Ideal'],ordered=True)  
#对切割程度有序排序
#对纯净程度进行排序
df.clarity = df.clarity.astype('category').cat.reorder_categories(['I1', 'SI2', 'SI1', 'VS2', 'VS1', 'VVS2', 'VVS1', 'IF'],ordered=True)
#组合降序排序
res = df.sort_values(['cut', 'clarity'], ascending=[False, True])
res.head(3)df.cut = df.cut.cat.reorder_categories(df.cut.cat.categories[::-1]) #df.cut.cat.categories[::-1] 让种类列表逆序排列,再根据逆序的种类排序
df.clarity = df.clarity.cat.reorder_categories(df.clarity.cat.categories[::-1])  df.cut = df.cut.cat.codes # 方法一:利用cat.codes,把分类标签转换为数值类型
clarity_cat = df.clarity.cat.categories
df.clarity = df.clarity.replace(dict(zip(clarity_cat, np.arange(len(clarity_cat))))) 
# 方法二:使用replace映射,类别封装成数值索引字典,然后转换成类别按分位数分割:
q = [0, 0.2, 0.4, 0.6, 0.8, 1]
point = [-np.infty, 1000, 3500, 5500, 18000, np.infty]
avg = df.price / df.caratdf['avg_cut'] = pd.cut(avg, bins=point, labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'])
df['avg_qcut'] = pd.qcut(avg, q=q, labels=['Very Low', 'Low', 'Mid', 'High', 'Very High'])
df.head()#查看有没有没有显示的种类
df.avg_cut.cat.categories
df.avg_cut.unique()
df.avg_cut = df.avg_cut.cat.remove_categories(['Very Low', 'Very High'])
df.avg_cut.head(3)按照分位数分箱
interval_avg = pd.IntervalIndex(pd.qcut(avg, q=q))  
右端点区间和长度
interval_avg.right.to_series().reset_index(drop=True).head(3)
左端点区间和长度
interval_avg.left.to_series().reset_index(drop=True).head(3)

时序数据

  1. 第一,会出现时间戳(Date times)的概念,即’2020-9-7 08:00:00’和’2020-9-7 10:00:00’这两个时间点分别代表了上课和下课的时刻,在pandas中称为Timestamp

    同时,一系列的时间戳可以组成DatetimeIndex,而将它放到Series中后,Series的类型就变为了datetime64[ns],如果有涉及时区则为datetime64[ns, tz],其中tz是timezone的简写。

  2. 第二,会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp做差就得到了时间差,pandas中利用Timedelta来表示。类似的,一系列的时间差就组成了TimedeltaIndex, 而将它放到Series中后,Series的类型就变为了timedelta64[ns]

  3. 第三,会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas利用Period来表示。类似的,一系列的时间段就组成了PeriodIndex, 而将它放到Series中后,Series的类型就变为了Period

  4. 第四,会出现日期偏置(Date offsets)的概念,假设你只知道9月的第一个周一早上8点要去上课,但不知道具体的日期,那么就需要一个类型来处理此类需求。再例如,想要知道2020年9月7日后的第30个工作日是哪一天,那么时间差就解决不了你的问题,从而pandas中的DateOffset就出现了。同时,pandas中没有为一列时间偏置专门设计存储类型,理由也很简单,因为需求比较奇怪,一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VGQctC38-1681776922755)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647523169516.png)]

由于时间段对象Period/PeriodIndex的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。

1. Timestamp的构造与属性

单个时间戳的生成利用pd.Timestamp实现,一般而言的常见日期格式都能被成功地转换:

ts = pd.Timestamp('2020-1-1 08:10:30')通过year, month, day, hour, min, second可以获取具体的数值:ts.year/month/day/hour/minute/second/minute/second

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jE75ToqA-1681776922756)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647523907198.png)]

pd.Timestamp.max
pd.Timestamp.min
pd.Timestamp.max.year - pd.Timestamp.min.year

2. Datetime序列的生成

一组时间戳可以组成时间序列,可以用to_datetimedate_range来生成。其中,to_datetime能够把一列时间戳格式的对象转换成为datetime64[ns]类型的时间序列:

pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])
df = pd.read_csv('../data/learn_pandas.csv')
s = pd.to_datetime(df.Test_Date)

在极少数情况,时间戳的格式不满足转换时,可以强制使用format进行匹配:

temp = pd.to_datetime(['2020\\\\1\\\\1','2020\\\\1\\\\3'],format='%Y\\\\%m\\\\%d')注意上面由于传入的是列表,而非pandas内部的Series,因此返回的是DatetimeIndex,如果想要转为datetime64[ns]的序列,需要显式用Series转化:
pd.Series(temp).head()另外,还存在一种把表的多列时间属性拼接转为时间序列的to_datetime操作,此时的列名必须和以下给定的时间关键词列名一致:
df_date_cols = pd.DataFrame({'year': [2020, 2020],'month': [1, 1],'day': [1, 2],'hour': [10, 20],'minute': [30, 50],'second': [20, 40]})
pd.to_datetime(df_date_cols)

date_range是一种生成连续间隔时间的一种方法,其重要的参数为start, end, freq, periods,它们分别表示开始时间,结束时间,时间间隔,时间戳个数。其中,四个中的三个参数决定了,那么剩下的一个就随之确定了。

这里要注意,开始或结束日期如果作为端点则它会被包含:

pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含

最后,要介绍一种改变序列采样频率的方法asfreq,它能够根据给定的freq对序列进行类似于reindex的操作:

s = pd.Series(np.random.rand(5), index=pd.to_datetime(['2020-1-%d'%i for i in range(1,10,2)]))s.asfreq('D') #精确到天数
s.asfreq('12H')#精确到小时

【NOTE】

前面提到了datetime64[ns]本质上可以理解为一个大整数,对于一个该类型的序列,可以使用max, min, mean,来取得最大时间戳、最小时间戳和“平均”时间戳。

3. dt对象

如同category, string的序列上定义了cat, str来完成分类数据和文本数据的操作,在时序类型的序列上定义了dt对象来完成许多时间序列的相关操作。这里对于datetime64[ns]类型而言,可以大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作。

第一类操作的常用属性包括:date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,其中daysinmonth, quarter分别表示该月一共有几天和季度。

在这些属性中,经常使用的是dayofweek,它返回了周中的星期情况,周一为0、周二为1,以此类推:

s.dt.dayofweek此外,可以通过month_name, day_name返回英文的月名和星期名,注意它们是方法而不是属性:
s.dt.month_name()
s.dt.day_name()

第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天:

s.dt.is_year_start # 还可选 is_quarter/month_start
s.dt.is_year_end # 还可选 is_quarter/month_end

第三类的取整操作包含round, ceil, floor,它们的公共参数为freq,常用的包括H, min, S(小时、分钟、秒),所有可选的freq可参考此处

s = pd.Series(pd.date_range('2020-1-1 20:35:00', '2020-1-1 22:35:00', freq='45min'))
ss.dt.round/ceil/floor('1H')  取整保留到小时

4. 时间戳的切片与索引

一般而言,时间戳序列作为索引使用。

如果想要选出某个子时间戳序列,

第一类方法是利用dt对象和布尔条件联合使用,

另一种方式是利用切片,后者常用于连续时间戳。下面,举一些例子说明:

s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))
idx = pd.Series(s.index).dt #日期索引器
Example1:每月的第一天或者最后一天
s[(idx.is_month_start|idx.is_month_end).values].head()Example2:双休日
s[idx.dayofweek.isin([5,6]).values].head()Example3:取出单日值
s['2020-01-01']
s['20200101'] # 自动转换标准格式Example4:取出七月
s['2020-07'].head()Example5:取出5月初至7月15日
s['2020-05':'2020-7-15'].head()

Timedelta的生成

正如在第一节中所说,时间差可以理解为两个时间戳的差,这里也可以通过pd.Timedelta来构造

pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')

生成时间差序列的主要方式是pd.to_timedelta,其类型为timedelta64[ns]

s = pd.to_timedelta(df.Time_Record)
s.head()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3RJpuurZ-1681776922757)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1647526158504.png)]

date_range一样,时间差序列也可以用timedelta_range来生成,它们两者具有一致的参数:

pd.timedelta_range('0s', '1000s', freq='6min')
pd.timedelta_range('0s', '1000s', periods=3)

对于Timedelta序列,同样也定义了dt对象,上面主要定义了的属性包括days, seconds, mircroseconds, nanoseconds,它们分别返回了对应的时间差特征。需要注意的是,这里的seconds不是指单纯的秒,而是对天数取余后剩余的秒数:

s.dt.seconds.head()
如果不想对天数取余而直接对应秒数,可以使用total_seconds
s.dt.total_seconds().head()与时间戳序列类似,取整函数也是可以在dt对象上使用的:
pd.to_timedelta(df.Time_Record).dt.round('min').head()

2. Timedelta的运算

时间差支持的常用运算有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:

td1 = pd.Timedelta(days=1)
td2 = pd.Timedelta(days=3)
ts = pd.Timestamp('20200101')td1 * 2
td2 - td1
ts + td1

这些运算都可以移植到时间差的序列上:

td1 = pd.timedelta_range(start='1 days', periods=5)
td2 = pd.timedelta_range(start='12 hours', freq='2H', periods=5)
ts = pd.date_range('20200101', '20200105')
td1 * 5td1 + pd.Timestamp('20200101')
td1 + ts # 逐个相加

四、日期偏置

1. Offset对象

日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求2020年9月第一个周一的日期,以及如何求2020年9月7日后的第30个工作日是哪一天。

pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)pd.Timestamp('20200907') + pd.offsets.BDay(30)

从上面的例子中可以看到,Offset对象在pd.offsets中被定义。当使用+时获取离其最近的下一个日期,当使用-时获取离其最近的上一个日期:

d.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)pd.Timestamp('20200907') - pd.offsets.BDay(30)pd.Timestamp('20200907') + pd.offsets.MonthEnd()

常用的日期偏置如下可以查阅这里的文档描述。在文档罗列的Offset中,需要介绍一个特殊的Offset对象CDay,其中的holidays, weekmask参数能够分别对自定义的日期和星期进行过滤,

holidays传入了需要过滤的日期列表,weekmask传入的是三个字母的星期缩写构成的星期字符串,其作用是只保留字符串中出现的星期

my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])
dr = pd.date_range('20200108', '20200111')
dr.to_series().dt.dayofweek
#结果显示中,没有了星期三和星期五,不计算这俩天

2. 偏置字符串

前面提到了关于date_rangefreq取值可用Offset对象,同时在pandas中几乎每一个Offset对象绑定了日期偏置字符串(frequencies strings/offset aliases),可以指定Offset对应的字符串来替代使用。下面举一些常见的例子。

pd.date_range('20200101','20200331', freq='MS') # 只生成月初
pd.date_range('20200101','20200331', freq='M') # 只生成月末
pd.date_range('20200101','20200110', freq='B') # 工作日
pd.date_range('20200101','20200201', freq='W-MON') # 周一,W代表week

时序中的滑窗与分组

1. 滑动窗口

所谓时序的滑窗函数,即把滑动窗口用freq关键词代替

下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N日均值线、N日均值加两倍N日标准差线、N日均值减两倍N日标准差线。利用rolling对象计算N=30BOLL指标可以如下写出:

import matplotlib.pyplot as plt
idx = pd.date_range('20200101', '20201231', freq='B')
np.random.seed(2020)
data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列
s = pd.Series(data,index=idx)
s.head()r = s.rolling('30D') #30天间隔的滑动窗口
plt.plot(s)
plt.title('BOLL LINES')
plt.plot(r.mean())
plt.plot(r.mean()+r.std()*2)
plt.plot(r.mean()-r.std()*2)

对于shift函数而言,作用在datetime64为索引的序列上时,可以指定freq单位进行滑动:

s.shift(freq='50D').head()

另外,datetime64[ns]的序列进行diff后就能够得到timedelta64[ns]的序列,这能够使用户方便地观察有序时间序列的间隔:

my_series = pd.Series(s.index)
my_series.diff(1).head()

2. 重采样

重采样对象resample和第四章中分组对象groupby的用法类似,前者是针对时间序列的分组计算而设计的分组对象。

例如,对上面的序列计算每10天的均值:

s.resample('10D').mean().head()

同时,如果没有内置定义的处理函数,可以通过apply方法自定义:

s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差

resample中要特别注意组边界值的处理情况,默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜00:00:00开始增加freq,直到不超过该最小时间戳的最大时间戳,由此对应的时间戳为起始值,然后每次累加freq参数作为分割结点进行分组,区间情况为左闭右开。

dx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')
data = np.random.randint(-1,2,len(idx)).cumsum()
s = pd.Series(data,index=idx)
s.head()下面对应的第一个组起始值为08:24:00,其是从当天0点增加72个freq=7 min得到的,如果再增加一个freq则超出了序列的最小时间戳08:26:35:
s.resample('7min').mean().head()  #从8:24开始

有时候,用户希望从序列的最小时间戳开始依次增加freq进行分组,此时可以指定origin参数为start

s.resample('7min', origin='start').mean().head()

在返回值中,要注意索引一般是取组的第一个时间戳,但M, A, Q, BM, BA, BQ, W这七个是取对应区间的最后一个时间戳:

太阳辐射数据集

  1. Datetime, Time合并为一个时间列Datetime,同时把它作为索引后排序。
  2. 每条记录时间的间隔显然并不一致,请解决如下问题:
  • 找出间隔时间的前三个最大值所对应的三组时间戳。
  • 是否存在一个大致的范围,使得绝大多数的间隔时间都落在这个区间中?如果存在,请对此范围内的样本间隔秒数画出柱状图,设置bins=50
  1. 求如下指标对应的Series
  • 温度与辐射量的6小时滑动相关系数
  • 以三点、九点、十五点、二十一点为分割,该观测所在时间区间的温度均值序列
  • 每个观测6小时前的辐射量(一般而言不会恰好取到,此时取最近时间戳对应的辐射量)
df['Date'] =  df.Data.str.extract('([/|\\w]+\\s).+')[0]+df.Timedf['Date'] = pd.to_datetime(df['Date'])
df = df.drop(columns=['Data','Time']).set_index('Date').sort_index()s = df.index.to_series().reset_index(drop=True).diff().dt.total_seconds()
max_3 = s.nlargest(3).index
df.index[max_3.union(max_3-1)]res = s.mask((s>s.quantile(0.99))|(s<s.quantile(0.01)))
_ = plt.hist(res, bins=50)res = df.Radiation.rolling('6H').corr(df.Temperature)
res.tail(3)res = df.Temperature.resample('6H', origin='03:00:00').mean()
res.head(3)my_dt = df.index.shift(freq='-6H')  #向前每六小时取一次
int_loc = [df.index.get_loc(i, method='nearest') for i in my_dt]  #遍历取到的时间索引去获取位置索引,时间索引对不上取最近的索引
res = df.Radiation.iloc[int_loc]   #通过取到的位置索引取数据
res.tail(3)

苹果销售数据集

  1. 统计如下指标:
  • 每月上半月(15号及之前)与下半月葡萄销量的比值
  • 每月最后一天的生梨销量总和
  • 每月最后一天工作日的生梨销量总和
  • 每月最后五天的苹果销量均值
  1. 按月计算周一至周日各品种水果的平均记录条数,行索引外层为水果名称,内层为月份,列索引为星期。
  2. 按天计算向前10个工作日窗口的苹果销量均值序列,非工作日的值用上一个工作日的结果填充。
# df.Date = pd.to_datetime(df.Date)
df_grape = df.query("Fruit == 'Grape'")
res = df_grape.groupby([np.where(df_grape.Date.dt.day<=15,'First', 'Second'), #利用np.where把一个月分成两组,给出命名df_grape.Date.dt.month])['Sale'].mean().to_frame().unstack(0) .droplevel(0,axis=1)
#unstack把first和second转成列索引,droplevel删除外层索引res = (res.First/res.Second).rename_axis('Month')
res.head()df[df.Date.dt.is_month_end].query("Fruit == 'Pear'").groupby('Date').Sale.sum().head()df[df.Date.isin(pd.date_range('20190101', '20191231',freq='BM'))].query("Fruit == 'Pear'").groupby('Date').Sale.mean().head()#选出时间,按照月份分组,取出每月的时间的最后5个
target_dt = df.drop_duplicates().groupby(df.Date.drop_duplicates().dt.month)['Date'].nlargest(5).reset_index(drop=True)
res = df.set_index('Date').loc[target_dt].reset_index().query("Fruit == 'Apple'") 
#最后5天苹果的销售量
res = res.groupby(res.Date.dt.month)['Sale'].mean().rename_axis('Month') 
#苹果的销售记录再按月分类,取均值
res.head()month_order = ['January','February','March','April','May','June','July','August','September','October','November','December']
week_order = ['Mon','Tue','Wed','Thu','Fri','Sat','Sum']
#把月份的名字分类然后转化为自定义的月份名字
group1 = df.Date.dt.month_name().astype('category').cat.reorder_categories(month_order, ordered=True)group2 = df.Fruit
# 生成序号数值和字符串的映射字典,把每周每天的名字转化为自定义的名字
group3 = df.Date.dt.dayofweek.replace(dict(zip(range(7),week_order))).astype('category').cat.reorder_categories(week_order, ordered=True)res = df.groupby([group1, group2,group3])['Sale'].count().to_frame().unstack(0).droplevel(0,axis=1)res.head()df_apple = df[(df.Fruit=='Apple')&(~df.Date.dt.dayofweek.isin([5,6]))]  # 非工作日的苹果销售记录
s = pd.Series(df_apple.Sale.values,index=df_apple.Date).groupby('Date').sum() # 对苹果销售记录,构造Series
res = s.rolling('10D').mean().reindex(pd.date_range('20190101','20191231')).fillna(method='ffill')  #重新构造连续时间戳,空值前向填充
res.head()

Matplotilb:

基础知识:

Figure的组成

现在我们来深入看一下figure的组成。通过一张figure解剖图,我们可以看到一个完整的matplotlib图像通常会包括以下四个层级,这些层级也被称为容器(container),下一节会详细介绍。在matplotlib的世界中,我们将通过各种命令方法来操纵图像中的每一个部分,从而达到数据可视化的最终效果,一副完整的图像实际上是各类子元素的集合。

  • Figure:顶层级,用来容纳所有绘图元素
  • Axes:matplotlib宇宙的核心,容纳了大量元素用来构造一幅幅子图,一个figure可以由一个或多个子图组成
  • Axis:axes的下属层级,用于处理所有和坐标轴,网格有关的元素
  • Tick:axis的下属层级,用来处理所有和刻度有关的元素

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oc5bW0xi-1681776922758)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1648899475619.png)]

matplotlib提供了两种最常用的绘图接口,在最后的思考题中请思考两种模式的主要区别

  1. 显式创建figure和axes,在上面调用绘图方法,也被称为OO模式(object-oriented style)
  2. 依赖pyplot自动创建figure和axes,并绘图
#第一种
x = np.linspace(0, 2, 100)fig, ax = plt.subplots()  
ax.plot(x, x, label='linear')  
ax.plot(x, x**2, label='quadratic')  
ax.plot(x, x**3, label='cubic')  
ax.set_xlabel('x label') 
ax.set_ylabel('y label') 
ax.set_title("Simple Plot")  
ax.legend() #第二种
x = np.linspace(0, 2, 100)plt.plot(x, x, label='linear') 
plt.plot(x, x**2, label='quadratic')  
plt.plot(x, x**3, label='cubic')
plt.xlabel('x label')
plt.ylabel('y label')
plt.title("Simple Plot")
plt.legend()

绘图模板

matplotlib提供了两种最常用的绘图接口,在最后的思考题中请思考两种模式的主要区别

  1. 显式创建figure和axes,在上面调用绘图方法,也被称为OO模式(object-oriented style)
  2. 依赖pyplot自动创建figure和axes,并绘图
# step1 准备数据
x = np.linspace(0, 2, 100)
y = x**2# step2 设置绘图样式,这一模块的扩展参考第五章进一步学习,这一步不是必须的,样式也可以在绘制图像是进行设置
mpl.rc('lines', linewidth=4, linestyle='-.')# step3 定义布局, 这一模块的扩展参考第三章进一步学习
fig, ax = plt.subplots()  # step4 绘制图像, 这一模块的扩展参考第二章进一步学习
ax.plot(x, y, label='linear')  # step5 添加标签,文字和图例,这一模块的扩展参考第四章进一步学习
ax.set_xlabel('x label') 
ax.set_ylabel('y label') 
ax.set_title("Simple Plot")  
ax.legend() 

matplotlib的三层api

matplotlib的原理或者说基础逻辑是,用Artist对象在画布(canvas)上绘制(Render)图形。
就和人作画的步骤类似:

  1. 准备一块画布或画纸
  2. 准备好颜料、画笔等制图工具
  3. 作画

所以matplotlib有三个层次的API:

matplotlib.backend_bases.FigureCanvas 代表了绘图区,所有的图像都是在绘图区完成的
matplotlib.backend_bases.Renderer 代表了渲染器,可以近似理解为画笔,控制如何在 FigureCanvas 上画图。
matplotlib.artist.Artist 代表了具体的图表组件,即调用了Renderer的接口在Canvas上作图。
前两者处理程序和计算机的底层交互的事项,第三项Artist就是具体的调用接口来做出我们想要的图,比如图形、文本、线条的设定。所以通常来说,我们95%的时间,都是用来和matplotlib.artist.Artist类打交道的。

Artist的分类

Artist有两种类型:primitivescontainers

primitive是基本要素,它包含一些我们要在绘图区作图用到的标准图形对象,如曲线Line2D,文字text,矩形Rectangle,图像image等。

container是容器,即用来装基本要素的地方,包括图形figure、坐标系Axes和坐标轴Axis。他们之间的关系如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jifQyhBI-1681776922759)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1648899793084.png)]

可视化中常见的artist类可以参考下图这张表格,解释下每一列的含义。
第一列表示matplotlib中子图上的辅助方法,可以理解为可视化中不同种类的图表类型,如柱状图,折线图,直方图等,这些图表都可以用这些辅助方法直接画出来,属于更高层级的抽象。

第二列表示不同图表背后的artist类,比如折线图方法plot在底层用到的就是Line2D这一artist类。

第三列是第二列的列表容器,例如所有在子图中创建的Line2D对象都会被自动收集到ax.lines返回的列表中。

下一节的具体案例更清楚地阐释了这三者的关系,其实在很多时候,我们只用记住第一列的辅助方法进行绘图即可,而无需关注具体底层使用了哪些类,但是了解底层类有助于我们绘制一些复杂的图表,因此也很有必要了解。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AQNc5onc-1681776922760)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1648899878845.png)]

基本元素 - primitives

各容器中可能会包含多种基本要素-primitives, 所以先介绍下primitives,再介绍容器。

本章重点介绍下 primitives 的几种类型:曲线-Line2D,矩形-Rectangle,多边形-Polygon,图像-image

1. 2DLines

在matplotlib中曲线的绘制,主要是通过类 matplotlib.lines.Line2D 来完成的。

matplotlib中线-line的含义:它表示的可以是连接所有顶点的实线样式,也可以是每个顶点的标记。此外,这条线也会受到绘画风格的影响,比如,我们可以创建虚线种类的线。

它的构造函数:

class matplotlib.lines.Line2D(xdata, ydata, linewidth=None, linestyle=None, color=None, marker=None, markersize=None, markeredgewidth=None, markeredgecolor=None, markerfacecolor=None, markerfacecoloralt='none', fillstyle=None, antialiased=None, dash_capstyle=None, solid_capstyle=None, dash_joinstyle=None, solid_joinstyle=None, pickradius=5, drawstyle=None, markevery=None, **kwargs)

其中常用的的参数有:

  • xdata:需要绘制的line中点的在x轴上的取值,若忽略,则默认为range(1,len(ydata)+1)
  • ydata:需要绘制的line中点的在y轴上的取值
  • linewidth:线条的宽度
  • linestyle:线型
  • color:线条的颜色
  • marker:点的标记,详细可参考markers API
  • markersize:标记的size

其他详细参数可参考Line2D官方文档

如何绘制lines

  1. 绘制直线line
  2. errorbar绘制误差折线图

介绍两种绘制直线line常用的方法:

  • plot方法绘制
  • Line2D对象绘制
# 1. plot方法绘制
x = range(0,5)
y1 = [2,5,7,8,10]
y2= [3,6,8,9,11]fig,ax= plt.subplots()
ax.plot(x,y1)
ax.plot(x,y2)
print(ax.lines); # 通过直接使用辅助方法画线,打印ax.lines后可以看到在matplotlib在底层创建了两个Line2D对象# 2. Line2D对象绘制
x = range(0,5)
y1 = [2,5,7,8,10]
y2= [3,6,8,9,11]
fig,ax= plt.subplots()
lines = [Line2D(x, y1), Line2D(x, y2,color='orange')]  # 显式创建Line2D对象
for line in lines:ax.add_line(line) # 使用add_line方法将创建的Line2D添加到子图中
ax.set_xlim(0,4)
ax.set_ylim(2, 11);

2) errorbar绘制误差折线图
pyplot里有个专门绘制误差线的功能,通过errorbar类实现,它的构造函数:

matplotlib.pyplot.errorbar(x, y, yerr=None, xerr=None, fmt=‘’, ecolor=None, elinewidth=None, capsize=None, barsabove=False, lolims=False, uplims=False, xlolims=False, xuplims=False, errorevery=1, capthick=None, *, data=None, **kwargs)

其中最主要的参数是前几个:

  • x:需要绘制的line中点的在x轴上的取值
  • y:需要绘制的line中点的在y轴上的取值
  • yerr:指定y轴水平的误差
  • xerr:指定x轴水平的误差
  • fmt:指定折线图中某个点的颜色,形状,线条风格,例如‘co–’
  • ecolor:指定error bar的颜色
  • elinewidth:指定error bar的线条宽度
fig = plt.figure()
x = np.arange(10)
y = 2.5 * np.sin(x / 20 * np.pi)
yerr = np.linspace(0.05, 0.2, 10)
plt.errorbar(x, y + 3, yerr=yerr, label='both limits (default)');

2. patches

matplotlib.patches.Patch类是二维图形类,并且它是众多二维图形的父类,它的所有子类见matplotlib.patches API ,
Patch类的构造函数:

Patch(edgecolor=None, facecolor=None, color=None, linewidth=None, linestyle=None, antialiased=None, hatch=None, fill=True, capstyle=None, joinstyle=None, **kwargs)

本小节重点讲述三种最常见的子类,矩形,多边形和楔型。

a. Rectangle-矩形

Rectangle矩形类在官网中的定义是: 通过锚点xy及其宽度和高度生成。 Rectangle本身的主要比较简单,即xy控制锚点,width和height分别控制宽和高。它的构造函数:

class matplotlib.patches.Rectangle(xy, width, height, angle=0.0, **kwargs)

在实际中最常见的矩形图是**hist直方图bar条形图**。

1) hist-直方图

matplotlib.pyplot.hist(x,bins=None,range=None, density=None, bottom=None, histtype=‘bar’, align=‘mid’, log=False, color=None, label=None, stacked=False, normed=None)

下面是一些常用的参数:

  • x: 数据集,最终的直方图将对数据集进行统计
  • bins: 统计的区间分布
  • range: tuple, 显示的区间,range在没有给出bins时生效
  • density: bool,默认为false,显示的是频数统计结果,为True则显示频率统计结果,这里需要注意,频率统计结果=区间数目/(总数*区间宽度),和normed效果一致,官方推荐使用density
  • histtype: 可选{‘bar’, ‘barstacked’, ‘step’, ‘stepfilled’}之一,默认为bar,推荐使用默认配置,step使用的是梯状,stepfilled则会对梯状内部进行填充,效果与bar类似
  • align: 可选{‘left’, ‘mid’, ‘right’}之一,默认为’mid’,控制柱状图的水平分布,left或者right,会有部分空白区域,推荐使用默认
  • log: bool,默认False,即y坐标轴是否选择指数刻度
  • stacked: bool,默认为False,是否为堆积状图

2) bar-柱状图

matplotlib.pyplot.bar(left, height, alpha=1, width=0.8, color=, edgecolor=, label=, lw=3)

下面是一些常用的参数:

  • left:x轴的位置序列,一般采用range函数产生一个序列,但是有时候可以是字符串
  • height:y轴的数值序列,也就是柱形图的高度,一般就是我们需要展示的数据;
  • alpha:透明度,值越小越透明
  • width:为柱形图的宽度,一般这是为0.8即可;
  • color或facecolor:柱形图填充的颜色;
  • edgecolor:图形边缘颜色
  • label:解释每个图像代表的含义,这个参数是为legend()函数做铺垫的,表示该次bar的标签

有两种方式绘制柱状图

  • bar绘制柱状图
  • Rectangle矩形类绘制柱状图
# bar绘制柱状图
y = range(1,17)
plt.bar(np.arange(16), y, alpha=0.5, width=0.5, color='yellow', edgecolor='red', label='The First Bar', lw=3);# Rectangle矩形类绘制柱状图
fig = plt.figure()
ax1 = fig.add_subplot(111)for i in range(1,17):rect =  plt.Rectangle((i+0.25,0),0.5,i)ax1.add_patch(rect)
ax1.set_xlim(0, 16)
ax1.set_ylim(0, 16);

b. Polygon-多边形

matplotlib.patches.Polygon类是多边形类。它的构造函数:

class matplotlib.patches.Polygon(xy, closed=True, **kwargs)

xy是一个N×2的numpy array,为多边形的顶点。
closed为True则指定多边形将起点和终点重合从而显式关闭多边形。

matplotlib.patches.Polygon类中常用的是fill类,它是基于xy绘制一个填充的多边形,它的定义:

matplotlib.pyplot.fill(*args, data=None, **kwargs)

参数说明 : 关于x、y和color的序列,其中color是可选的参数,每个多边形都是由其节点的x和y位置列表定义的,后面可以选择一个颜色说明符。您可以通过提供多个x、y、[颜色]组来绘制多个多边形。

# 用fill来绘制图形
x = np.linspace(0, 5 * np.pi, 1000) 
y1 = np.sin(x)
y2 = np.sin(2 * x) 
plt.fill(x, y1, color = "g", alpha = 0.3);

Wedge-契形

matplotlib.patches.Polygon类是多边形类。其基类是matplotlib.patches.Patch,它的构造函数:

class matplotlib.patches.Wedge(center, r, theta1, theta2, width=None, **kwargs)

一个Wedge-契形 是以坐标x,y为中心,半径为r,从θ1扫到θ2(单位是度)。
如果宽度给定,则从内半径r -宽度到外半径r画出部分楔形。wedge中比较常见的是绘制饼状图。

matplotlib.pyplot.pie语法:

matplotlib.pyplot.pie(x, explode=None, labels=None, colors=None, autopct=None, pctdistance=0.6, shadow=False, labeldistance=1.1, startangle=0, radius=1, counterclock=True, wedgeprops=None, textprops=None, center=0, 0, frame=False, rotatelabels=False, *, normalize=None, data=None)

制作数据x的饼图,每个楔子的面积用x/sum(x)表示。
其中最主要的参数是前4个:

  • x:契型的形状,一维数组。
  • explode:如果不是等于None,则是一个len(x)数组,它指定用于偏移每个楔形块的半径的分数。
  • labels:用于指定每个契型块的标记,取值是列表或为None。
  • colors:饼图循环使用的颜色序列。如果取值为None,将使用当前活动循环中的颜色。
  • startangle:饼状图开始的绘制的角度。
labels = 'Frogs', 'Hogs', 'Dogs', 'Logs'
sizes = [15, 30, 45, 10] 
explode = (0, 0.1, 0, 0) fig1, ax1 = plt.subplots() 
ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', shadow=True, startangle=90) 
ax1.axis('equal'); # Equal aspect ratio ensures that pie is drawn as a circle. 

3. collections

collections类是用来绘制一组对象的集合,collections有许多不同的子类,如RegularPolyCollection, CircleCollection, Pathcollection, 分别对应不同的集合子类型。其中比较常用的就是散点图,它是属于PathCollection子类,scatter方法提供了该类的封装,根据x与y绘制不同大小或颜色标记的散点图。 它的构造方法:

Axes.scatter(self, x, y, s=None, c=None, marker=None, cmap=None, norm=None, vmin=None, vmax=None, alpha=None, linewidths=None, verts=, edgecolors=None, *, plotnonfinite=False, data=None, **kwargs)

其中最主要的参数是前5个:

  • x:数据点x轴的位置
  • y:数据点y轴的位置
  • s:尺寸大小
  • c:可以是单个颜色格式的字符串,也可以是一系列颜色
  • marker: 标记的类型
# 用scatter绘制散点图
x = [0,2,4,6,8,10] 
y = [10]*len(x) 
s = [20*2**n for n in range(len(x))] 
plt.scatter(x,y,s=s) 

4. images

images是matplotlib中绘制image图像的类,其中最常用的imshow可以根据数组绘制成图像,它的构造函数:

class matplotlib.image.AxesImage(ax, cmap=None, norm=None, interpolation=None, origin=None, extent=None, filternorm=True, filterrad=4.0, resample=False, **kwargs)

imshow根据数组绘制图像

matplotlib.pyplot.imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=None, vmin=None, vmax=None, origin=None, extent=None, shape=, filternorm=1, filterrad=4.0, imlim=, resample=None, url=None, *, data=None, **kwargs)

使用imshow画图时首先需要传入一个数组,数组对应的是空间内的像素位置和像素点的值,interpolation参数可以设置不同的差值方法,具体效果如下。

methods = [None, 'none', 'nearest', 'bilinear', 'bicubic', 'spline16','spline36', 'hanning', 'hamming', 'hermite', 'kaiser', 'quadric','catrom', 'gaussian', 'bessel', 'mitchell', 'sinc', 'lanczos']grid = np.random.rand(4, 4)fig, axs = plt.subplots(nrows=3, ncols=6, figsize=(9, 6),subplot_kw={'xticks': [], 'yticks': []})for ax, interp_method in zip(axs.flat, methods):ax.imshow(grid, interpolation=interp_method, cmap='viridis')ax.set_title(str(interp_method))plt.tight_layout();

三、对象容器 - Object container

容器会包含一些primitives,并且容器还有它自身的属性。
比如Axes Artist,它是一种容器,它包含了很多primitives,比如Line2DText;同时,它也有自身的属性,比如xscal,用来控制X轴是linear还是log的。

1. Figure容器

matplotlib.figure.FigureArtist最顶层的container-对象容器,它包含了图表中的所有元素。一张图表的背景就是在Figure.patch的一个矩形Rectangle。 当我们向图表添加Figure.add_subplot()或者Figure.add_axes()元素时,这些都会被添加到Figure.axes列表中。

fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
for ax in fig.axes:ax.grid(True)

Figure也有它自己的text、line、patch、image。你可以直接通过add primitive语句直接添加。但是注意Figure默认的坐标系是以像素为单位,你可能需要转换成figure坐标系:(0,0)表示左下点,(1,1)表示右上点。

Figure容器的常见属性:
Figure.patch属性:Figure的背景矩形
Figure.axes属性:一个Axes实例的列表(包括Subplot)
Figure.images属性:一个FigureImages patch列表
Figure.lines属性:一个Line2D实例的列表(很少使用)
Figure.legends属性:一个Figure Legend实例列表(不同于Axes.legends)
Figure.texts属性:一个Figure Text实例列表

2. Axes容器

matplotlib.axes.Axes是matplotlib的核心。大量的用于绘图的Artist存放在它内部,并且它有许多辅助方法来创建和添加Artist给它自己,而且它也有许多赋值方法来访问和修改这些Artist

Figure容器类似,Axes包含了一个patch属性,对于笛卡尔坐标系而言,它是一个Rectangle;对于极坐标而言,它是一个Circle。这个patch属性决定了绘图区域的形状、背景和边框。

fig = plt.figure()
ax = fig.add_subplot(111)
rect = ax.patch  # axes的patch是一个Rectangle实例
rect.set_facecolor('green') #设置背景图片为绿色

Axes容器的常见属性有:
artists: Artist实例列表
patch: Axes所在的矩形实例
collections: Collection实例
images: Axes图像
legends: Legend 实例
lines: Line2D 实例
patches: Patch 实例
texts: Text 实例
xaxis: matplotlib.axis.XAxis 实例
yaxis: matplotlib.axis.YAxis 实例

matplotlib.axis.Axis实例处理tick linegrid linetick label以及axis label的绘制,它包括坐标轴上的刻度线、刻度label、坐标网格、坐标轴标题。通常你可以独立的配置y轴的左边刻度以及右边的刻度,也可以独立地配置x轴的上边刻度以及下边的刻度。

刻度包括主刻度和次刻度,它们都是Tick刻度对象。

Axis也存储了用于自适应,平移以及缩放的data_intervalview_interval。它还有Locator实例和Formatter实例用于控制刻度线的位置以及刻度label。

每个Axis都有一个label属性,也有主刻度列表和次刻度列表。这些ticksaxis.XTickaxis.YTick实例,它们包含着line primitive以及text primitive用来渲染刻度线以及刻度文本。

axis = ax.xaxis # axis为X轴对象
axis.get_ticklocs()     # 获取刻度线位置
axis.get_ticklabels()   
# 获取刻度label列表(一个Text实例的列表)。 可以通过minor=True|False关键字参数控制输出minor还是major的tick label。
axis.get_ticklines()    
# 获取刻度线列表(一个Line2D实例的列表)。 可以通过minor=True|False关键字参数控制输出minor还是major的tick line。
axis.get_data_interval()# 获取轴刻度间隔
axis.get_view_interval()# 获取轴视角(位置)的间隔
下面的例子展示了如何调整一些轴和刻度的属性(忽略美观度,仅作调整参考):fig = plt.figure() # 创建一个新图表
rect = fig.patch   # 矩形实例并将其设为黄色
rect.set_facecolor('lightgoldenrodyellow')
​
ax1 = fig.add_axes([0.1, 0.3, 0.4, 0.4]) # 创一个axes对象,从(0.1,0.3)的位置开始,宽和高都为0.4,
rect = ax1.patch   # ax1的矩形设为灰色
rect.set_facecolor('lightslategray')
​
​
for label in ax1.xaxis.get_ticklabels(): # 调用x轴刻度标签实例,是一个text实例label.set_color('red') # 颜色label.set_rotation(45) # 旋转角度label.set_fontsize(16) # 字体大小
​
for line in ax1.yaxis.get_ticklines():# 调用y轴刻度线条实例, 是一个Line2D实例line.set_color('green')    # 颜色line.set_markersize(25)    # marker大小line.set_markeredgewidth(2)# marker粗细

4. Tick容器

matplotlib.axis.Tick是从FigureAxesAxisTick中最末端的容器对象。
Tick包含了tickgrid line实例以及对应的label

所有的这些都可以通过Tick的属性获取,常见的tick属性有
Tick.tick1line:Line2D实例
Tick.tick2line:Line2D实例
Tick.gridline:Line2D实例
Tick.label1:Text实例
Tick.label2:Text实例

y轴分为左右两个,因此tick1对应左侧的轴;tick2对应右侧的轴。
x轴分为上下两个,因此tick1对应下侧的轴;tick2对应上侧的轴。

fig, ax = plt.subplots()
ax.plot(100*np.random.rand(20))
# ax2.plot(10*np.random.rand(20))
# 设置ticker的显示格式
formatter = matplotlib.ticker.FormatStrFormatter('$%1.2f')
ax.yaxis.set_major_formatter(formatter)# 设置ticker的参数,右侧为主轴,颜色为绿色
ax.yaxis.set_tick_params(which='major', labelcolor='green',labelleft=False, labelright=True);

一、Figure和Axes上的文本

Matplotlib具有广泛的文本支持,包括对数学表达式的支持、对栅格和矢量输出的TrueType支持、具有任意旋转的换行分隔文本以及Unicode支持。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fGa7tmfs-1681776922762)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1648904841655.png)]

fig = plt.figure()
ax = fig.add_subplot()# 分别为figure和ax设置标题,注意两者的位置是不同的
fig.suptitle('bold figure suptitle', fontsize=14, fontweight='bold')
ax.set_title('axes title')# 设置x和y轴标签
ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')# 设置x和y轴显示范围均为0到10
ax.axis([0, 10, 0, 10])# 在子图上添加文本
ax.text(3, 8, 'boxed italics text in data coords', style='italic',bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})# 在画布上添加文本,一般在子图上添加文本是更常见的操作,这种方法很少用
fig.text(0.4,0.8,'This is text for figure')ax.plot([2], [1], 'o')
# 添加注解
ax.annotate('annotate', xy=(2, 1), xytext=(3, 4),arrowprops=dict(facecolor='black', shrink=0.05));

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-chmKX36o-1681776922763)(C:%5CUsers%5C%E9%93%B6%E6%99%97%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1648905033932.png)]

2.text - 子图上的文本

text的调用方式为Axes.text(x, y, s, fontdict=None, **kwargs)
其中x,y为文本出现的位置,默认状态下即为当前坐标系下的坐标值,
s为文本的内容,
fontdict是可选参数,用于覆盖默认的文本属性,
**kwargs为关键字参数,也可以用于传入文本样式参数

重点解释下fontdict和**kwargs参数,这两种方式都可以用于调整呈现的文本样式,最终效果是一样的,不仅text方法,其他文本方法如set_xlabel,set_title等同样适用这两种方式修改样式。通过一个例子演示这两种方法是如何使用的。

fig = plt.figure(figsize=(10,3))
axes = fig.subplots(1,2)# 使用关键字参数修改文本样式
axes[0].text(0.3, 0.8, 'modify by **kwargs', style='italic',bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10});# 使用fontdict参数修改文本样式 ,就是提前把参数设置写成字典
font = {'bbox':{'facecolor': 'red', 'alpha': 0.5, 'pad': 10}, 'style':'italic'}
axes[1].text(0.3, 0.8, 'modify by fontdict', fontdict=font);

3.xlabel和ylabel - 子图的x,y轴标签

xlabel的调用方式为Axes.set_xlabel(xlabel, fontdict=None, labelpad=None, *, loc=None, **kwargs)
ylabel方式类似,这里不重复写出。
其中xlabel即为标签内容,
fontdict**kwargs用来修改样式,上一小节已介绍,
labelpad为标签和坐标轴的距离,默认为4,
loc为标签位置,可选的值为’left’, ‘center’, 'right’之一,默认为居中

# 观察labelpad和loc参数的使用效果
fig = plt.figure(figsize=(10,3))
axes = fig.subplots(1,2)
axes[0].set_xlabel('xlabel',labelpad=20,loc='left')# loc参数仅能提供粗略的位置调整,如果想要更精确的设置标签的位置,可以使用position参数+horizontalalignment参数来定位
# position由一个元组过程,第一个元素0.2表示x轴标签在x轴的位置,第二个元素对于xlabel其实是无意义的,随便填一个数都可以
# horizontalalignment='left'表示左对齐,这样设置后x轴标签就能精确定位在x=0.2的位置处
axes[1].set_xlabel('xlabel', position=(0.2, _), horizontalalignment='left');

4.title和suptitle - 子图和画布的标题

title的调用方式为Axes.set_title(label, fontdict=None, loc=None, pad=None, *, y=None, **kwargs)
其中label为子图标签的内容,fontdict,loc,**kwargs和之前小节相同不重复介绍
pad是指标题偏离图表顶部的距离,默认为6
y是title所在子图垂向的位置。默认值为1,即title位于子图的顶部。

suptitle的调用方式为figure.suptitle(t, **kwargs)
其中t为画布的标题内容

# 观察pad参数的使用效果
fig = plt.figure(figsize=(10,3))
fig.suptitle('This is figure title',y=1.2) # 通过参数y设置高度
axes = fig.subplots(1,2)
axes[0].set_title('This is title',pad=15)
axes[1].set_title('This is title',pad=6);

5.annotate - 子图的注解

annotate的调用方式为Axes.annotate(text, xy, *args, **kwargs)
其中text为注解的内容,
xy为注解箭头指向的坐标,
其他常用的参数包括:
xytext为注解文字的坐标,
xycoords用来定义xy参数的坐标系,
textcoords用来定义xytext参数的坐标系,
arrowprops用来定义指向箭头的样式
annotate的参数非常复杂,这里仅仅展示一个简单的例子,更多参数可以查看官方文档中的annotate介绍

fig = plt.figure()
ax = fig.add_subplot()
ax.annotate("",xy=(0.2, 0.2), xycoords='data',xytext=(0.8, 0.8), textcoords='data',arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=0.2"));

6.字体的属性设置

字体设置一般有全局字体设置和自定义局部字体设置两种方法。

#该block讲述如何在matplotlib里面,修改字体默认属性,完成全局字体的更改。
plt.rcParams['font.sans-serif'] = ['SimSun']    # 指定默认字体为新宋体。
plt.rcParams['axes.unicode_minus'] = False      # 解决保存图像时 负号'-' 显示为方块和报错的问题。#局部字体的修改方法1
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
plt.plot(x, label='小示例图标签')# 直接用字体的名字
plt.xlabel('x 轴名称参数', fontproperties='Microsoft YaHei', fontsize=16)         # 设置x轴名称,采用微软雅黑字体
plt.ylabel('y 轴名称参数', fontproperties='Microsoft YaHei', fontsize=14)         # 设置Y轴名称
plt.title('坐标系的标题',  fontproperties='Microsoft YaHei', fontsize=20)         # 设置坐标系标题的字体
plt.legend(loc='lower right', prop={"family": 'Microsoft YaHei'}, fontsize=10) ;   # 小示例图的字体设置
#一般绘图时会自动创建刻度,而如果通过上面的例子使用set_ticks创建刻度可能会导致tick的范围与所绘制图形的范围不一致的问题。
#所以在下面的案例中,axs[1]中set_xtick的设置要与数据范围所对应,然后再通过set_xticklabels设置刻度所对应的标签
import numpy as np
import matplotlib.pyplot as plt
fig, axs = plt.subplots(2, 1, figsize=(6, 4), tight_layout=True)
x1 = np.linspace(0.0, 6.0, 100)
y1 = np.cos(2 * np.pi * x1) * np.exp(-x1)
axs[0].plot(x1, y1)
axs[0].set_xticks([0,1,2,3,4,5,6])axs[1].plot(x1, y1)
axs[1].set_xticks([0,1,2,3,4,5,6])#要将x轴的刻度放在数据范围中的哪些位置
axs[1].set_xticklabels(['zero','one', 'two', 'three', 'four', 'five','six'],#设置刻度对应的标签rotation=30, fontsize='small')#rotation选项设定x刻度标签倾斜30度。
axs[1].xaxis.set_ticks_position('bottom')#set_ticks_position()方法是用来设置刻度所在的位置,常用的参数有bottom、top、both、none
print(axs[1].xaxis.get_ticklines());

2.Tick Locators and Formatters

除了上述的简单模式,还可以使用Tick Locators and Formatters完成对于刻度位置和刻度标签的设置。 其中Axis.set_major_locator和Axis.set_minor_locator方法用来设置标签的位置,Axis.set_major_formatter和Axis.set_minor_formatter方法用来设置标签的格式。这种方式的好处是不用显式地列举出刻度值列表。

set_major_formatter和set_minor_formatter这两个formatter格式命令可以接收字符串格式(matplotlib.ticker.StrMethodFormatter)或函数参数(matplotlib.ticker.FuncFormatter)来设置刻度值的格式 。

# 接收字符串格式的例子
fig, axs = plt.subplots(2, 2, figsize=(8, 5), tight_layout=True)
for n, ax in enumerate(axs.flat):ax.plot(x1*10., y1)formatter = matplotlib.ticker.FormatStrFormatter('%1.1f')
axs[0, 1].xaxis.set_major_formatter(formatter)formatter = matplotlib.ticker.FormatStrFormatter('-%1.1f')
axs[1, 0].xaxis.set_major_formatter(formatter)formatter = matplotlib.ticker.FormatStrFormatter('%1.5f')
axs[1, 1].xaxis.set_major_formatter(formatter);# 接收函数的例子
def formatoddticks(x, pos):"""Format odd tick positions."""if x % 2:return f'{x:1.2f}'else:return ''fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
ax.plot(x1, y1)
ax.xaxis.set_major_formatter(formatoddticks);

b) Tick Locators

在普通的绘图中,我们可以直接通过上图的set_ticks进行设置刻度的位置,缺点是需要自己指定或者接受matplotlib默认给定的刻度。当需要更改刻度的位置时,matplotlib给了常用的几种locator的类型。如果要绘制更复杂的图,可以先设置locator的类型,然后通过axs.xaxis.set_major_locator(locator)绘制即可
locator=plt.MaxNLocator(nbins=7)
locator=plt.FixedLocator(locs=[0,0.5,1.5,2.5,3.5,4.5,5.5,6])#直接指定刻度所在的位置
locator=plt.AutoLocator()#自动分配刻度值的位置
locator=plt.IndexLocator(offset=0.5, base=1)#面元间距是1,从0.5开始
locator=plt.MultipleLocator(1.5)#将刻度的标签设置为1.5的倍数
locator=plt.LinearLocator(numticks=5)#线性划分5等分,4个刻度

# 接收各种locator的例子
fig, axs = plt.subplots(2, 2, figsize=(8, 5), tight_layout=True)
for n, ax in enumerate(axs.flat):ax.plot(x1*10., y1)locator = matplotlib.ticker.AutoLocator()
axs[0, 0].xaxis.set_major_locator(locator)locator = matplotlib.ticker.MaxNLocator(nbins=10)
axs[0, 1].xaxis.set_major_locator(locator)locator = matplotlib.ticker.MultipleLocator(5)
axs[1, 0].xaxis.set_major_locator(locator)locator = matplotlib.ticker.FixedLocator([0,7,14,21,28])
axs[1, 1].xaxis.set_major_locator(locator);

此外matplotlib.dates 模块还提供了特殊的设置日期型刻度格式和位置的方式

# 特殊的日期型locator和formatter
locator = mdates.DayLocator(bymonthday=[1,15,25])
formatter = mdates.DateFormatter('%b %d')fig, ax = plt.subplots(figsize=(5, 3), tight_layout=True)
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
base = datetime.datetime(2017, 1, 1, 0, 0, 1)
time = [base + datetime.timedelta(days=x) for x in range(len(x1))]
ax.plot(time, y1)
ax.tick_params(axis='x', rotation=70);

三、legend(图例)

在具体学习图例之前,首先解释几个术语:

legend entry(图例条目)

每个图例由一个或多个legend entries组成。一个entry包含一个key和其对应的label。

legend key(图例键)

每个 legend label左面的colored/patterned marker(彩色/图案标记)

legend label(图例标签)

描述由key来表示的handle的文本

legend handle(图例句柄)

用于在图例中生成适当图例条目的原始对象

以下面这个图为例,右侧的方框中的共有两个legend entry;两个legend key,分别是一个蓝色和一个黄色的legend key;两个legend label,一个名为‘Line up’和一个名为‘Line Down’的legend label

fig, ax = plt.subplots()
line_up, = ax.plot([1, 2, 3], label='Line 2')
line_down, = ax.plot([3, 2, 1], label='Line 1')
ax.legend(handles = [line_up, line_down], labels = ['Line Up', 'Line Down']);
fig,axes = plt.subplots(1,4,figsize=(10,4))
for i in range(4):axes[i].plot([0.5],[0.5])axes[i].legend(labels='a',loc=i)  # 观察loc参数传入不同值时图例的位置
fig.tight_layout()
lengend中loc位置参数
Location String   Location Code===============   ============='best'            0'upper right'     1'upper left'      2'lower left'      3'lower right'     4'right'           5'center left'     6'center right'    7'lower center'    8'upper center'    9'center'          10
设置图例边框及背景

In [165]:

fig = plt.figure(figsize=(10,3))
axes = fig.subplots(1,3)
for i, ax in enumerate(axes):ax.plot([1,2,3],label=f'ax {i}')
axes[0].legend(frameon=False) #去掉图例边框
axes[1].legend(edgecolor='blue') #设置图例边框颜色
axes[2].legend(facecolor='gray'); #设置图例背景颜色,若无边框,参数无效
fig,ax =plt.subplots()
ax.plot([1,2,3],label='label')
ax.legend(title='legend title');

1.matplotlib预先定义样式

matplotlib贴心地提供了许多内置的样式供用户使用,使用方法很简单,只需在python脚本的最开始输入想使用style的名称即可调用,尝试调用不同内置样式,比较区别

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as npplt.style.use('default')
plt.plot([1,2,3,4],[2,3,4,5])
plt.style.use('ggplot')
plt.plot([1,2,3,4],[2,3,4,5])print(plt.style.available)--------------------------------
2.用户自定义stylesheet
在任意路径下创建一个后缀名为mplstyle的样式清单,编辑文件添加以下样式内容axes.titlesize : 24
axes.labelsize : 20
lines.linewidth : 3
lines.markersize : 10
xtick.labelsize : 16
ytick.labelsize : 16plt.style.use('file/presentation.mplstyle')
plt.plot([1,2,3,4],[2,3,4,5])plt.style.use('default') # 恢复到默认样式
plt.plot([1,2,3,4],[2,3,4,5])mpl.rc('lines', linewidth=4, linestyle='-.')
plt.plot([1,2,3,4],[2,3,4,5])

二、matplotlib的色彩设置(color)

在可视化中,如何选择合适的颜色和搭配组合也是需要仔细考虑的,色彩选择要能够反映出可视化图像的主旨。
从可视化编码的角度对颜色进行分析,可以将颜色分为色相、亮度和饱和度三个视觉通道。通常来说:
色相: 没有明显的顺序性、一般不用来表达数据量的高低,而是用来表达数据列的类别。
明度和饱和度: 在视觉上很容易区分出优先级的高低、被用作表达顺序或者表达数据量视觉通道。
具体关于色彩理论部分的知识,不属于本教程的重点,请参阅有关拓展材料学习。
ECharts数据可视化实验室
学会这6个可视化配色基本技巧,还原数据本身的意义

在matplotlib中,设置颜色有以下几种方式:

# 颜色用[0,1]之间的浮点数表示,四个分量按顺序分别为(red, green, blue, alpha),其中alpha透明度可省略
plt.plot([1,2,3],[4,5,6],color=(0.1, 0.2, 0.5))
plt.plot([4,5,6],[1,2,3],color=(0.1, 0.2, 0.5, 0.5))

3.灰度色阶

In [27]:

# 当只有一个位于[0,1]的值时,表示灰度色阶
plt.plot([1,2,3],[4,5,6],color='0.5')

4.单字符基本颜色

In [28]:

# matplotlib有八个基本颜色,可以用单字符串来表示,分别是'b', 'g', 'r', 'c', 'm', 'y', 'k', 'w',对应的是blue, green, red, cyan, magenta, yellow, black, and white的英文缩写
plt.plot([1,2,3],[4,5,6],color='m')

6.使用colormap设置一组颜色

有些图表支持使用colormap的方式配置一组颜色,从而在可视化中通过色彩的变化表达更多信息。

在matplotlib中,colormap共有五种类型:

  • 顺序(Sequential)。通常使用单一色调,逐渐改变亮度和颜色渐渐增加,用于表示有顺序的信息
  • 发散(Diverging)。改变两种不同颜色的亮度和饱和度,这些颜色在中间以不饱和的颜色相遇;当绘制的信息具有关键中间值(例如地形)或数据偏离零时,应使用此值。
  • 循环(Cyclic)。改变两种不同颜色的亮度,在中间和开始/结束时以不饱和的颜色相遇。用于在端点处环绕的值,例如相角,风向或一天中的时间。
  • 定性(Qualitative)。常是杂色,用来表示没有排序或关系的信息。
  • 杂色(Miscellaneous)。一些在特定场景使用的杂色组合,如彩虹,海洋,地形等。
x = np.random.randn(50)
y = np.random.randn(50)
plt.scatter(x,y,c=x,cmap='RdPu')

import matplotlib,pyplot as plt

import numpy as np

x=np.linspace(-1,1,50)

y=2*x+1 / x**x

plt.plot(x,y)

plt.show() 输出的是一个坐标轴

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pIt9HR1a-1681776922764)(C:\\Users\\银晗\\AppData\\Roaming\\Typora\\typora-user-images\\1617109108983.png)]

y1=2*x+1 y2=x**2

plt.figure()

plt.plot(x,y1)

plt.figure()

plt.plot(x,y2) plt.show() 显示两张图一次函数和二次函数

plt.figure(num=3,figsize=(8,5),color=‘red’,linewidth=1.0,linestyle=‘ --’) 图的长8和宽5,x轴±3距离,线的颜色,线的宽度,线样式

折线图:

#简单
plt.figure(figsize=(8,10),dpi=80)	dpi是清晰度
plt.plot([1,2,3,4,5,6,7]#这是x轴坐标,[12,34,42,13,42,21,32]#y轴坐标)
plt.show()
plt.savefig("test.png")/也可以鼠标右键保存
#进阶
import random
x = range(60)# 60个数据
y = [random.uniform(15,18) for i in x]  #列表表达式plt.figure(figsize=(20,10),dpi=80)
plt.plot(x,y)x_label= ["11点{}分".format(i) for i in x]
#设置x,y轴的步长[::5]就是步长为5,本意就是原列表替换 
plt.xticks(range(0,60,5),x_label[::5])
plt.yticks(range(0,40,4))plt.grid(True,linestyle='--',alpha=0.5#透明度)		#grid()网格

设置坐标轴:

坐标轴有多长:plt.xlim((-1,2)) ylim((-2,3))

坐标轴的名字:plt.xlabel(“ …”) plt. ylabel (‘i am x’) plt.title()

设置坐标轴的单位下标:

new_ticks=np.linspace(-1,2,5) plt.xticks(new_ticks)

横坐标从-1到2分5个单位也就0.25一个单位

plt.yticks([-2,-1.8,-1,1.22,3,],

​ [‘really bad’,‘bad’,‘normal’,‘good’,‘really good’])

将y轴上对应的标点换成文字

移动坐标轴:

ax=plt.gca()

ax.spines[‘right’].set_color(‘none’) 图的四个边框

ax.spines[‘top’].set_color(‘none’) 让边框消失

ax.xaixs.set_ticks_potion(‘bottom’)

ax.yaxis.set_ticks_potion(‘left’)

ax.spines[‘bottom’].set_position((‘data’,0))

ax.spines[‘left’].set_position((‘data’,0))

plt.show()

l1,=plt.plot(x,y1,label=‘up’)

l2,=plt.plot(x,y2,color=‘red’,linestyle=‘–’,label=‘down’)

plt.legend(handles(操作对象)=[l1,l2],labels(名字)=aaa,loc=‘best/upper right’)

设置背景透明度,避免遮挡

for label in ax.get_xticklabels()+ax.get_yticklabels():
label.set_fontsize(12)
label.set_bbox(dict(facecolor='white',edgecolor='None',alpha=0.7#这个是控制透明度))

散点图:

n=1024
X=np.random.normol(0,1,n)
Y=np.random.normal(0,1,n)
T=np.arctan2(Y,X)#颜色plt.scatter(X,Y,size=75,color=T,alpha=0.5)plt.xlim((-1.5,1.5))
plt.ylim((-1.5,1.5))
plt.xtick(())#隐藏ticks
ply.ytick(())
ply.show()

饼图:

arr = [11,58,22,5]	#整数会计算百分比arr = [0.1,0.4,0.2]	#都是小数直接等于百分比,饼图可不完整arr = [11,58,22,5,22]
plt.pie(arr,labels=['a','b','c','d','e'],labeldistance=0.3,autopct= '%.2f%%')#labels是标签,labeldistance是标签距离圆心的距离,autipct =  '% 几位小数f%%'arr = [11,58,22,5,22]
plt.pie(arr,labels=['a','b','c','d','e'],labeldistance=0.3,autopct= '%.2f%%',shadow=True,explode=[0.4,0.3,0.2,0.1,0.2])#shadow是显示阴影,explode是分裂开plt.pie(arr)
plt.show()

柱状图:

堆叠柱状图

import numpy as np
import matplotlib.pyplot as pltN = 5
menMeans = (20, 35, 30, 35, 27)
womenMeans = (25, 32, 34, 20, 25)
menStd = (2, 3, 4, 1, 2)
womenStd = (3, 5, 2, 3, 3)
ind = np.arange(N)    # the x locations for the groups
width = 0.35       # the width of the bars: can also be len(x) sequencep1 = plt.bar(ind, menMeans, width, yerr=menStd)
p2 = plt.bar(ind, womenMeans, width,bottom=menMeans, yerr=womenStd)plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(np.arange(0, 81, 10))
plt.legend((p1[0], p2[0]), ('Men', 'Women'))plt.show()

奥运会奖牌柱状图

n=12	#柱状图的数量
X=np.arrange(n)
Y1=(1-X/float(n))*np.random.uniform(0.5,1.0,n)
Y2=(1-X/float(n))*np.random.uniform(0.5,1.0,n)
#X就是随机产生0-12的数字,uniform根据X的值随机产生一个y
plt.bar(X,+Y1,facecolor='#9999ff',edgecolor='white')
plt.bar(X,-Y2)  #bar就是柱状图,正负号表示向上向下
#其他的都是渲染
for x ,y in zip(X,Y1):plt.text(x+0.4,y+0.05,%.2f%y,ha='center',va='bottom')  #以text的方式给柱状图加标注,ha是顶部对齐,va是底部对齐plt.figure(figsize=(10,10))
bar_width=0.2
plt.bar(x=range(10),height=gold,label='金牌',color='red',width=bar_width)
plt.bar(x=np.arange(0,10,1)+bar_width,height=silver,label='银牌',color='blue',width=bar_width)
plt.bar(x=np.arange(0,10,1)+bar_width*2,height=bronze,label='铜牌',color='c',width=bar_width)for x,y in enumerate(gold):plt.text(x,y,'%s'%y,ha='center',va='bottom')
for x,y in enumerate(silver):plt.text(x+bar_width,y,'%s'%y,ha='center',va='bottom')
for x,y in enumerate(bronze):plt.text(x+bar_width*2,y,'%s'%y,ha='center',va='top')plt.title('奥运奖牌分析')
plt.ylabel('奖牌数')
plt.xlabel('国家/地区')
plt.xticks(np.arange(0,10,1)+bar_width/3,label)
plt.xticks(rotation=25)
plt.legend()
plt.show()    

等高线:

def f(x,y):return(1-x/2+x**5+y**3)*np.exp(-x**2-y**2)
#计算高度的公式n=256
x=np.linspace(-3,3,n)
y=np.linspace(-3,3,n)
X,Y=np.meshgrid(x,y)#图的网格的
plt.contourf(X,Y,f(X,Y),8,alpha=0.75,cmap=plt.cm.hot这个是让数字对应颜色,坐标表示的区域显示什么颜色)C=plt.contour(X,Y,f(X,Y),8,colors='black',linewidth=.5)  #画等高线,这个数字 8 就是等高线将图分为几部分,上面也一样
plt.clabel(C,inline=True,fontsize=10)
#等高线数字描述

打印图片:

import matplotlib.pyplot as plt
img_arr = plt.imread(“./1.jpg”)
#imread将图片读取到矩阵里,矩阵的数据代表了图片

#imshow将numpy可视化展示
plt.imshow(img_arr)

a=np.array([...数字]),reshpe(3,3)
plt.imageshow(a,interpolation='nearest',cmap='bone',origin='lower')
#interpolation就是图片显示的样式,lower是从第低到高
plt.colorbar(shrink=0.9)#控制颜色的柱状图
plt.xticks(())
plt.yticks(())
plt.show()
from mpl_toolkits.mplot3d import Axes3D
fig=plt.figure()
ax=Axes3D(fig)
#X,Y value
X=np.arange(-4,4,0.25)
Y=np.arange(-4,4,0.25)
X,Y=np.meshgrid(x,y)
r=np.sqrt(x**2+Y**2)
#height value
Z=np.sin(R)
ax.plot_surface(X,Y,Z,rstride=1(行跨度),cstride=5(列跨度),cmap=plt.get_cmap('rainbow'))
ax.contourf(X,Y,zdir='z'(将图像投影到哪个平面上xyz),offset=-2,cmap='rainbow')
ax.set_zlim=(-2,2)  #z轴的高度
plt.show()
import matplotlib.pyplot as pltplt.figure()plt.subplot(2(2),2(2),1)
plt.plot([0,1],[0,2])plt.subplot(2,2,2)
plt.plot([0,1],[0,2])plt.subplot(223)
plt.plot([0,1],[0,3])
#以此类推 
plt.show()
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
#method 1: subplot2gridax1=plt.subplot2grid((3,3),(0,0),colspan=3(),rowspan=1())
ax1.plot([1,2],[1,2])
ax1.set_title('ax1_titile')	#set是设置的意识
ax2=plt.subplot2grid((3,3),(1,0),colspan=2) 
#colspan,rowspan是控制坐标轴的长和宽#method 2: gridspec
plt.figure()
gs=gridspec.GridSpec(3,3)  #这个也是控制长和宽
ax1=plt.subplot(gs[0,:])
ax2=plt.subplot(gs[1,:2])
#method 3: easy to define structure
plt
plt.tight_layout()
plt.show()

堆叠柱状图

  • yerr:标准差的竖线
  • 堆叠图,第二个及以后需要有button参数连接
import numpy as np
import matplotlib.pyplot as pltN = 5
menMeans = (20, 35, 30, 35, 27)
womenMeans = (25, 32, 34, 20, 25)menStd = (2, 3, 4, 1, 2)
womenStd = (3, 5, 2, 3, 3)ind = np.arange(N)    # the x locations for the groups
width = 0.35       # the width of the bars: can also be len(x) sequencep1 = plt.bar(ind, menMeans, width, yerr=menStd)
p2 = plt.bar(ind, womenMeans, width,bottom=menMeans, yerr=womenStd)# p1 = plt.bar(ind, menMeans, width)
# p2 = plt.bar(ind, womenMeans, width,   
#              bottom=menMeans)                 plt.xlabel('Groups')
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(np.arange(0, 81, 10))
plt.legend((p1[0], p2[0]), ('Men', 'Women'))plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xkjgwLlK-1681776922766)(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAX4AAAEWCAYAAABhffzLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAf3klEQVR4nO3de7xVZb3v8c9XQFEkFVoSVyFFRS4hLkRCiy2J2i4vtS01DbxRHdupWYqeV2q9tnvbzm7H09bNMZW0yAuZaDeIJLeBF1BSEQwvS0BRLkoqCon+zh/jWThZrMtcuMacC8b3/XrN1xz38RvPmus3n/mMMZ6hiMDMzIpjp2oHYGZmleXEb2ZWME78ZmYF48RvZlYwTvxmZgXjxG9mVjBO/NZuSRoraUW149ieSLpC0i3tII6Jku6vdhzWOCf+gpF0uKS5kv4u6RVJf5E0stpxmVnldKx2AFY5kj4A3AN8BbgN2Bk4AtjYxvvpEBHvtOU2K0lSx4jYVO04imp7//xsD1zjL5b9ASJiWkS8ExFvRcTMiHisfgFJ50haLOl1SU9KGpGmD5I0R9I6SYskHVeyzk2SrpX0W0nrgX+S1EvSdEmrJT0n6Wslyx8qab6k1yS9LOkHzQUt6VJJayTVSfpCmjYyrduxZLnPSlrYxDa6S7o77fNhSf9W2hQhKSSdK2kpsLSkLJ5Ov4xmSOqVpvdPy5fue46ks9PwxPRL6pr0y2qJpHHNHN9kSc+UlPmJJfMmSrpf0tWSXk1leWzJ/AGS/pzWnQV8sIWyvEjSSkkvSjo7Hcd+ad4uaT/LUtleJ2nXNG+spBWSLpS0Km3jjAblOyOV70PAvg32e6CkWaksn5L0uZJ5W31+mjsGawMR4VdBXsAHgLXAVOBYYK8G808CXgBGAgL2A/YBOgFPA5eS/Uo4EngdOCCtdxPwd2AMWWViN2ABcFla/sPAs8DRafl5wOlpeHfgsCbiHQtsAn4A7AJ8HFhfst8ngWNLlr8TuLCJbf0yvXYDDgKWA/eXzA9gFtAN2DUd4xpgRNr3NcB9adn+afmOJevPAc5OwxNT3Beksvt8Kp9uTcR2EtArld3n0zH2LNnW28A5QAeyX2svAiopy/ry+Vj6u9zSxH6OAV4CBqdyuDkdx35p/o+AGakMugJ3A//R4G/xnXRMnwTeJH2GUtneBnQBhpB9ju5P87qk8j6DrJVhRCrbwU18fjpX+39lR39VPQC/KvwHh0HpH21F+keeAfRI8/4AnNfIOkekhLFTybRpwBVp+CbgZyXzRgHLGmzjEuDGNHwf8G3ggy3EWp9supRMuw34Vhq+GPh5Gu6WElHPRrbTISXPA0qm/RtbJ/4jS8Z/CvxnyfjuaRv9KS/xb07OadpDpC+7Mv5GC4HjS7b1dMm83dK+PwT0a6R8fkHTif8GUiJP4/ulbe1H9kW/Hti3ZP5o4LmSv8VbDY55FXBYSfkeWDLv33kv8X8e+J8Gsfw3cHljnx+/8n+5qadgImJxREyMiD5kNbNeZDU9gL7AM42s1gtYHhHvlkx7HuhdMr68ZHgfoFdqFlonaR3Zr4Ueaf5ZZM1OS1Kzy6eaCfnViFjfYL+90vAtwKcl7Q58jiy5rGxkGzVkNc3SGJc3slzptF5pXwBExBtkv5Z6N1ypCS9EymqNxL0FSV+UtLCkrIawZZPNSyVxvJkGd0/ba6x8mtKLpsughvRLrSSO36fp9dbGluc+3kxxNFa+pXHsA4xq8Hn4AtmXV2OxWM58crfAImKJpJuAL6VJy2nQNpu8CPSVtFNJ8u8H/K10cyXDy8lqigOb2O9S4BRJOwGfAe6Q1L1BAqu3l6QuJfP6AU+k7bwgaR5wInA6cG0Th7qarGbcpyTmvo2FVjL8IlnCAkBSF6A7WRNGfSy7Aa+l4dIkBtBbkkqSfz+yX1dbkLQP8P+AccC8iHgnnadQE8dSaiWNl09TXe6uJCuDeqVlsIasRj84Il4oY9+l6su3L7CkJI56y4E/R8RRzWzD3QRXkGv8BZJOsF0oqU8a7wucAjyQFrke+IakQ5TZLyWmB8mS3UWSOkkaC3yarF23MQ8Br0m6WNKukjpIGqJ02aik0yTVpC+RdWmd5q7i+LaknSUdAXwKuL1k3s+Ai4ChZG38W4nsCpFfAVdI2k3SgcAXm9kfZE0mZ0gaLmkXsqaLByOiLiJWk30BnJaO7Uy2/sLcG/haKq+TyJrYftvIfrqQJb3VAOmE6ZAWYqs/rueB+bxXPoeT/V2acls6pkGSdiM7B1O/rXfJvoB+KGnvFEtvSUeXEUfD8j0ImFCyyD3A/pJOT+XRSdnJ+UHlHKe1PSf+YnmdrP39wXT1xANktecLASLiduBKsqT3OvBrshOS/wCOIzshvAb4L+CLEbGk4Q7Sdt4hS0DDgefSOtcDe6RFjgEWSXoD+DFwckRsaCLml4BXyWrgPwe+3GC/d5LVzO9s4hdDva+m/b9EdlJzGs1cxhoRs4FvAdPJasr7AieXLHIO8E2y5p/BwNwGm3gQGEh27FcC/xIRaxvZz5PA98lO0r5M9gX2l2aOo6FTyf6mrwCXk30RNnVMvwP+D3Av2cn6eWlWfTlcnKY/IOk14I/AAWXG8VWyZp+XyNrsbyzZ7+vAeLLyezEt812yE9JWBYrwLyzbvkl6BvhSRPyxFet8F/hQRExoceHWxzOR7ETv4W297baUatxPALuE71soFNf4bbsm6bNkTSV/amG5AyUNS01Yh5KdYG60aWhHJunE1Cy0F1mt+24n/eLJNfFLukDZzT5PSJomqbOkbulGjqXpfa88Y7Adl6Q5ZCd0z21wxVFjupK1Q68na+v+PnBXrgG2T18iO5/wDNl5la9UNxyrhtyaeiT1Bu4HDoqItyTdRnZy6yDglYi4StJkshtALs4lCDMz20reTT0dgV2V3dq+G9mJnePJ7hwlvZ+QcwxmZlYit+v40zXWVwPLyK4PnhkRMyX1qL/JJiJW1l861pCkScAkgC5duhxy4IEH5hWqmdkOacGCBWsioqbh9NwSf2q7Px4YQHat9u2STit3/YiYAkwBqK2tjfnz5+cRppnZDktSo3dy59nU8wmyuzdXR8TbZCfWPgq8LKlnCqonWX8fZmZWIXkm/mXAYelOPpHdkr6Y7Lb1+munJ1DMKyvMzKomzzb+ByXdATxC1o/Ho2RNN7sDt0k6i+zL4aS8YjAzs63l2klbRFxOdht5qY1ktX8zsxa9/fbbrFixgg0bmurVwzp37kyfPn3o1KlTWcu7d04za9dWrFhB165d6d+/P1mrsZWKCNauXcuKFSsYMGBAWeu4ywYza9c2bNhA9+7dnfSbIInu3bu36heRE7+ZtXtO+s1rbfk48ZuZFYzb+M1su9J/8m/adHt1V/1zi8tI4rTTTuPmm28GYNOmTfTs2ZNRo0Zxzz33tGk8leAav5lZC7p06cITTzzBW2+9BcCsWbPo3bvcxy+3P078ZmZlOPbYY/nNb7JfG9OmTeOUU07ZPG/9+vWceeaZjBw5koMPPpi77sruS73pppv4zGc+wzHHHMPAgQO56KKLqhJ7Q078ZmZlOPnkk/nlL3/Jhg0beOyxxxg1atTmeVdeeSVHHnkkDz/8MPfeey/f/OY3Wb8+exLowoULufXWW3n88ce59dZbWb58ebUOYTO38ZuZlWHYsGHU1dUxbdo0PvnJT24xb+bMmcyYMYOrr74ayC5BXbZsGQDjxo1jjz2yx00fdNBBPP/88/Tt27eywTfgxG9mVqbjjjuOb3zjG8yZM4e1a9dunh4RTJ8+nQMO2PLZ9A8++CC77PLeM+U7dOjApk3Vf9Klm3rMzMp05plnctlllzF06NAtph999NFcc8011D/R8NFHH61GeGVzjd/MtivlXH6Zlz59+nDeeedtNf1b3/oW559/PsOGDSMi6N+/f7u+zDO3Z+62JT+Ixay4Fi9ezKBBg6odRrvXWDlJWhARtQ2XdVOPmVnBOPGbmRWME7+ZWcE48VuLxo4dy9ixY6sdhpm1ESd+M7OCyS3xSzpA0sKS12uSzpfUTdIsSUvT+155xWBmZlvL82HrTwHDASR1AF4A7gQmA7Mj4ipJk9P4xXnFYWY7mCv2aOPt/b3FRS644AL22Wcfzj//fCC7Yatv375cf/31AFx44YX07t2br3/9620bW04q1dQzDngmIp4HjgempulTgRMqFIOZ2Tb56Ec/yty5cwF49913WbNmDYsWLdo8f+7cuYwZM6Za4bVapRL/ycC0NNwjIlYCpPe9KxSDmdk2GTNmzObEv2jRIoYMGULXrl159dVX2bhxI4sXL2bdunUcfPDBDB06lDPPPJONGzcC0L9/fy699FJGjx5NbW0tjzzyCEcffTT77rsv11133eZ9fO9732PkyJEMGzaMyy+/HIC6ujoGDRrEOeecw+DBgxk/fvzmZwK8H7knfkk7A8cBt7dyvUmS5kuav3r16nyCMzMrQ69evejYsSPLli1j7ty5jB49mlGjRjFv3jzmz5/P/vvvz9lnn725++VNmzZx7bXXbl6/b9++zJs3jyOOOIKJEydyxx138MADD3DZZZcBWe+eS5cu5aGHHmLhwoUsWLCA++67D4ClS5dy7rnnsmjRIvbcc0+mT5/+vo+nEjX+Y4FHIuLlNP6ypJ4A6X1VYytFxJSIqI2I2pqamgqEaWbWtPpaf33iHz169Obx3r17M2DAAPbff38AJkyYsDlxQ9arJ8DQoUMZNWoUXbt2paamhs6dO7Nu3TpmzpzJzJkzOfjggxkxYgRLlixh6dKlAAwYMIDhw4cDcMghh1BXV/e+j6USif8U3mvmAZgBTEjDE4C7KhCDmdn7Ut/O//jjjzNkyBAOO+ww5s2bx9y5cxkxYkSz69Z3zbzTTjtt0U3zTjvtxKZNm4gILrnkEhYuXMjChQt5+umnOeuss7ZYF9quW+dcE7+k3YCjgF+VTL4KOErS0jTvqjxjMDNrC2PGjOGee+6hW7dudOjQgW7durFu3TrmzZvHGWecQV1dHU8//TQAN998Mx//+MfL3vbRRx/NDTfcwBtvvAHACy+8wKpVjTaGtIlcu2WOiDeB7g2mrSW7ysfMrPXKuPwyD0OHDmXNmjWceuqpW0x744036NOnDzfeeCMnnXQSmzZtYuTIkXz5y18ue9vjx49n8eLFjB49GoDdd9+dW265hQ4dOrT5cYC7ZbYy1HfXMGfOnKrGYcXkbpnL426ZzcysSU78ZmYF40cvFlVrbnuvW9/6daBqbbG244kIJFU7jHartU32rvGbWbvWuXNn1q5d2+rkVhQRwdq1a+ncuXPZ67jGb7aNfNK7Mvr06cOKFSvwHfxN69y5M3369Cl7eSd+M2vXOnXqxIABA6odxg7FTT1mZgXjxG9mVjBO/GZmBePEb2ZWMD65a1bK9zdYAbjGb2ZWME78ZmYF48RvZlYwTvxmZgXjk7vWojkTu1Q7hHbJ5WLbK9f4zcwKxonfzKxg8n7Y+p6S7pC0RNJiSaMldZM0S9LS9L5XnjGYmdmW8q7x/xj4fUQcCHwEWAxMBmZHxEBgdho3M7MKyS3xS/oA8DHgpwAR8Y+IWAccD0xNi00FTsgrBjMz21qeNf4PA6uBGyU9Kul6SV2AHhGxEiC9793YypImSZovab4fwGBm1nbyTPwdgRHAtRFxMLCeVjTrRMSUiKiNiNqampq8YjQzK5w8E/8KYEVEPJjG7yD7InhZUk+A9L4qxxjMzKyB3BJ/RLwELJd0QJo0DngSmAFMSNMmAHflFYOZmW0t7zt3/xX4uaSdgWeBM8i+bG6TdBawDDgp5xjMzKxErok/IhYCtY3MGpfnfs2sesaOHQvAnDlzqhqHNc137pqZFYwTv5lZwTjxm5kVjBO/mVnBOPGbmRWME7+ZWcE48ZuZFYwfvWhmLbtij/KXrVvf+nUArvh765bfzrSn+xtc4zczKxgnfjOzgnHiNzMrGCd+M7OCceI3MysYJ34zs4Jx4jczKxhfx29mbWrOxC7VDqFyttP7G1zjNzMrGCd+M7OCybWpR1Id8DrwDrApImoldQNuBfoDdcDnIuLVPOMwM7P3VKLG/08RMTwi6p+9OxmYHREDgdlp3MzMKqQaTT3HA1PT8FTghCrEYGZWWHkn/gBmSlogaVKa1iMiVgKk970bW1HSJEnzJc1fvXp1zmGamRVH3pdzjomIFyXtDcyStKTcFSNiCjAFoLa2NvIK0MysaHKt8UfEi+l9FXAncCjwsqSeAOl9VZ4xmJnZlnJL/JK6SOpaPwyMB54AZgAT0mITgLvyisHMrL2YM7FLu7m5Lc+mnh7AnZLq9/OLiPi9pIeB2ySdBSwDTsoxBjMzayC3xB8RzwIfaWT6WmBcXvt9P9rTo9HMzPLiO3fNzArGid/MrGCc+M3MCsaJ38ysYJz4zcwKxonfzKxgnPjNzAqmrOv4Je0LrIiIjZLGAsOAn0XEuvxCayPb6aPRzMzyUm6NfzrwjqT9gJ8CA4Bf5BaVmZnlptzE/25EbAJOBH4UERcAPfMLy8zM8lJu4n9b0ilknardk6Z1yickMzPLU7mJ/wxgNHBlRDwnaQBwS35hmZlZXso6uRsRT0q6GOiXxp8DrsozMDMzy0dZNX5JnwYWAr9P48MlzcgxLjMzy0m5TT1XkD09ax1ARCwku7LHzMy2M+Um/k0R0fBidT8H18xsO1Tug1iekHQq0EHSQOBrwNz8wqqO9vJYNDOzPJVb4/9XYDCwkezGrb8D5+cUk5mZ5ajFGr+kDsCMiPgE8L9bu4O0/nzghYj4lKRuwK1Af6AO+FxEvNra7ZqZ2bZpscYfEe8Ab0pqZQc2m50HLC4ZnwzMjoiBwOw0bmZmFVJuG/8G4HFJs4D19RMj4mvNrSSpD/DPwJXA19Pk44GxaXgqMAe4uOyIzczsfSk38f8mvVrrR8BFQNeSaT0iYiVARKyUtHdjK0qaBEwC6Nev3zbs2szMGlPunbtTJe0M7J8mPRURbze3jqRPAasiYkHqyrlVImIKMAWgtrbWl46ambWRcvvjH0vWLFMHCOgraUJE3NfMamOA4yR9EugMfEDSLcDLknqm2n5PYNX7iN/MzFqp3Ms5vw+Mj4iPR8THgKOBHza3QkRcEhF9IqI/cDLwp4g4DZhB1ssn6f2ubYrczMy2SbmJv1NEPFU/EhF/Y9u7Zb4KOErSUuAo3NmbmVlFlXtyd76knwI3p/EvAAvK3UlEzCG7eoeIWAuMKz9EMzNrS+Um/q8A55J11SDgPuC/8grK2pexN2VX8LpLC7MdQ7mJvyPw44j4AWy+G3eX3KIyM7PclNvGPxvYtWR8V+CPbR+OmZnlrdzE3zki3qgfScO75ROSmZnlqdzEv17SiPoRSbXAW/mEZGZmeSq3jf984HZJL5I9gKUX8Pm8gjKz7ZcvBmj/mq3xSxop6UMR8TBwIFl3ypvInr37XAXiMzOzNtZSU89/A/9Iw6OBS4GfAK+S+tExM7PtS0tNPR0i4pU0/HlgSkRMB6ZLWphrZJar/ht+UfayL707Oa3Tupus61q1tLVn/rzsWFqq8XeQVP/lMA74U8m8cs8PmJlZO9JS8p4G/FnSGrKreP4HQNJ+ZM/dNSssn8Rs3IdOdfdb7V2ziT8irpQ0G+gJzIyI+n7xdyJ7ALuZmW1nWmyuiYgHGpn2t3zCMTOzvJV7A5eZme0gnPjNzArGid/MrGB8Saa1yFdpmO1YXOM3MyuY3BK/pM6SHpL0V0mLJH07Te8maZakpel9r7xiMDOzreVZ498IHBkRHwGGA8dIOgyYDMyOiIFkD3iZnGMMZmbWQG6JPzL1D2/plF4BHA9MTdOnAifkFYOZmW0t15O76dm8C4D9gJ9ExIOSekTESoCIWClp7ybWnQRMAujXr1+eYZpt5s7IrAhyPbkbEe9ExHCgD3CopCGtWHdKRNRGRG1NTU1uMZqZFU1FruqJiHXAHOAY4GVJPQHS+6pKxGBmZpk8r+qpkbRnGt4V+ASwBJgBTEiLTQDuyisGM7P2YuxN6zf36Fptebbx9wSmpnb+nYDbIuIeSfOA2ySdBSwDTsoxBjMzayC3xB8RjwEHNzJ9LdlDXczMrAp8566ZWcE48ZuZFYw7aTMz20bb630frvGbmRWME7+ZWcG4qcdsG/k5Bba9co3fzKxgnPjNzArGTT1mZhXQnpoGXeM3MysYJ34zs4Jx4jczKxgnfjOzgnHiNzMrGCd+M7OCceI3MysYJ/4S7enRaGZmeXHiNzMrmDwftt5X0r2SFktaJOm8NL2bpFmSlqb3vfKKwczMtpZnjX8TcGFEDAIOA86VdBAwGZgdEQOB2WnczMwqJLfEHxErI+KRNPw6sBjoDRwPTE2LTQVOyCsGMzPbWkU6aZPUHzgYeBDoERErIftykLR3E+tMAiYB9OvXb5v3vb0+Gs3MLC+5n9yVtDswHTg/Il4rd72ImBIRtRFRW1NTk1+AZmYFk2vil9SJLOn/PCJ+lSa/LKlnmt8TWJVnDGZmtqU8r+oR8FNgcUT8oGTWDGBCGp4A3JVXDGZmtrU82/jHAKcDj0tamKZdClwF3CbpLGAZcFKOMZiZWQO5Jf6IuB9QE7PH5bVfMzNrnh+9WKI9PRrNzCwv7rLBzKxgnPjNzArGid/MrGCc+M3MCsaJ38ysYJz4zcwKxonfzKxgnPjNzArGid/MrGCc+M3MCsaJ38ysYJz4zcwKxonfzKxgnPjNzArGid/MrGCc+M3MCsaJ38ysYPJ82PoNklZJeqJkWjdJsyQtTe975bV/MzNrXJ41/puAYxpMmwzMjoiBwOw0bmZmFZRb4o+I+4BXGkw+HpiahqcCJ+S1fzMza1yl2/h7RMRKgPS+d4X3b2ZWeO325K6kSZLmS5q/evXqaodjZrbDqHTif1lST4D0vqqpBSNiSkTURkRtTU1NxQI0M9vRVTrxzwAmpOEJwF0V3r+ZWeHleTnnNGAecICkFZLOAq4CjpK0FDgqjZuZWQV1zGvDEXFKE7PG5bVPMzNrWbs9uWtmZvlw4jczKxgnfjOzgnHiNzMrGCd+M7OCceI3MysYJ34zs4Jx4jczKxgnfjOzgnHiNzMrGCd+M7OCceI3MysYJ34zs4Jx4jczKxgnfjOzgnHiNzMrGCd+M7OCceI3MysYJ34zs4KpSuKXdIykpyQ9LWlyNWIwMyuqiid+SR2AnwDHAgcBp0g6qNJxmJkVVTVq/IcCT0fEsxHxD+CXwPFViMPMrJAUEZXdofQvwDERcXYaPx0YFRFfbbDcJGBSGj0AeKpCIX4QWFOhfW1PXC6Nc7k0zuXSuEqXyz4RUdNwYscKBlBPjUzb6tsnIqYAU/IPZ0uS5kdEbaX32965XBrncmmcy6Vx7aVcqtHUswLoWzLeB3ixCnGYmRVSNRL/w8BASQMk7QycDMyoQhxmZoVU8aaeiNgk6avAH4AOwA0RsajScTSj4s1L2wmXS+NcLo1zuTSuXZRLxU/umplZdfnOXTOzgnHiNzMrmEInfkk9JP1C0rOSFkiaJ+lESd0l3SvpDUn/t9pxVloz5XJUGn88vR9Z7VgrqZlyOVTSwvT6q6QTqx1rJTVVLiXz+6X/pW9UM85Ka+bz0l/SWyWfmesqHVs1ruNvFyQJ+DUwNSJOTdP2AY4DNgDfAoakV2G0UC73A5+OiBclDSE7Qd+7WrFWUgvl8gegNl240BP4q6S7I2JT1QKukBbKpd4Pgd9VPrrqaaFcHgWeiYjh1YqvsIkfOBL4R0Rs/raNiOeBa9Lo/ZL2q0pk1dVSudRbBHSWtEtEbKxkgFVSbrl0ppEbEndgzZaLpBOAZ4H1VYmueposF0n9qxZVUuSmnsHAI9UOoh0qt1w+CzxakKQPLZSLpFGSFgGPA18uQm0/abJcJHUBLga+XdGI2oeW/o8GSHpU0p8lHVGpoOoVuca/BUk/AQ4n+5YeWe142ovGykXSYOC7wPhqxlZNDcslIh4EBksaBEyV9LuI2FDdKCuvtFyAPwM/jIg3spaP4mpQLocD/SJiraRDgF9LGhwRr1UqniLX+BcBI+pHIuJcYBywVYdGBdNsuUjqA9wJfDEinqlKhNVR1uclIhaTNWsU5dxQc+UyCvhPSXXA+cCl6ebNImiyXCJiY0SsTdMXAM8A+1cyuCIn/j+RtVF/pWTabtUKph1pslwk7Qn8BrgkIv5ShdiqqblyGSCpYxreh6w32bqKR1gdTZZLRBwREf0joj/wI+DfI6IoV8k193mpSc8lQdKHgYFk50EqptB37qYrMH5IVjNZTVZTuy4ibk21lA8AOwPrgPER8WSVQq2opsqF7AN6CbC0ZPHxEbGq4kFWQTPlsjMwGXgbeBf4TkT8ukphVlxz/0cly1wBvBERV1clyCpo5vOyCfhOen8HuDwi7q5obEVO/GZmRVTkph4zs0Jy4jczKxgnfjOzgnHiNzMrGCd+M7OCceK3QmupZ0mzHZETvxVWSQ+K90XEhyPiELJnQPdpsJy7NrEdihO/FVmjPShGxDWSJkq6XdLdwExJ3ST9WtJjkh6QNAyyG5NK+5mX9ETqb72/pCWSpqZ17pBUf+fmVZKeTNMLc0OTtR+uyViRtdSD4mhgWES8Iukast5IT1D2AJqfAcNb2P4BwFkR8RdJNwD/K72fCBwYEZG6wTCrKNf4zRJJP0lP0Ho4TZoVEa+k4cOBmwEi4k9Ad0l7tLDJ5SV9Gt2StvEa2YN+rpf0GeDNNj0IszI48VuRtdTjZunDQxrrVzjI+lsp/T/q3GD+FsunfvoPBaYDJwC/35bAzd4PJ34rstb00Hof8AUASWOBNan/9DrSl4ekEcCAknX6SRqdhk8he6rb7sAeEfFbsq6Kh7fBcZi1ijtps0JrpgfFXcmeo/vVtFw34EayxP4mMCkiHpO0K3AXsDfwMFlzzrFp878l+8L4KFmPpqcDe6TlO5P9irg6Iqbmf6Rm73HiN8tBeq7qPRFRlAey2HbETT1mZgXjGr+ZWcG4xm9mVjBO/GZmBePEb2ZWME78ZmYF48RvZlYw/x/5hTJLPB4p1wAAAABJRU5ErkJggg== )]

多条柱状图

In [11]:

import numpy as np
import matplotlib.pyplot as pltmen_means, men_std = (20, 35, 30, 35, 27), (2, 3, 4, 1, 2)
women_means, women_std = (25, 32, 34, 20, 25), (3, 5, 2, 3, 3)ind = np.arange(len(men_means))  # the x locations for the groups
width = 0.35  # the width of the barsfig, ax = plt.subplots()rects1 = ax.bar(ind - width/2, men_means, width, yerr=men_std,   #两个柱图,宽度就除以2color='SkyBlue', label='Men')
rects2 = ax.bar(ind + width/2, women_means, width, yerr=women_std,color='IndianRed', label='Women')# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind)
ax.set_xticklabels(('G1', 'G2', 'G3', 'G4', 'G5'))
ax.legend()def autolabel(rects, xpos='center'):"""Attach a text label above each bar in *rects*, displaying its height.  柱状图的上面显示文本*xpos* indicates which side to place the text w.r.t. the center ofthe bar. It can be one of the following {'center', 'right', 'left'}.  参数可选:左、中、右"""xpos = xpos.lower()  # normalize the case of the parameterha = {'center': 'center', 'right': 'left', 'left': 'right'}offset = {'center': 0.5, 'right': 0.57, 'left': 0.43}  # x_txt = x + w*offfor rect in rects:height = rect.get_height()ax.text(rect.get_x() + rect.get_width()*offset[xpos], 1.01*height,'{}'.format(height), ha=ha[xpos], va='bottom')autolabel(rects1, "left")
autolabel(rects2, "right")plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HOZztXVV-1681776922767)(data:image/png;base64,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 )]

水平柱状图

直接用barh画就行了

In [140]:

import matplotlib.pyplot as plt
import numpy as np# Fixing random state for reproducibility
np.random.seed(19680801)plt.rcdefaults()
fig, ax = plt.subplots()# Example data
people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim')
y_pos = np.arange(len(people))
performance = 3 + 10 * np.random.rand(len(people))
error = np.random.rand(len(people))ax.barh(y_pos, performance, xerr=error, align='center')
# ax.set_yticks(y_pos, labels=people)
ax.invert_yaxis()  # labels read top-to-bottom
ax.set_xlabel('Performance')
ax.set_title('How fast do you want to go today?')plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BY7YqCZc-1681776922769)(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAhkAAAHFCAYAAABfDutCAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAyGklEQVR4nO3de3zO9f/H8ee107UZW79NM2MYyTGHKDmUMyFf+oVfKSl+SQ45lK+kQn2z6Fvy7TA5lxQpRE7NmZA5lfCtr8KItZI2jc1s798f3Xb9umxj13jvMh732+26uX3e1/vzfr+uz+dy7bnPYZfDGGMEAABwhfl4uwAAAHBtImQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkwM3s2bPlcDi0Y8eOPJ+/5557VKlSpaItKg+//fab7r//fkVERMjhcKhr165XdPwzZ85o7NixWr9+/WWNs379ejkcjsseBxd3/PhxjR07Vnv27ClQ//3792vs2LE6fPiw1bps2bJli8aOHavff//9io/dokULtWjR4oqPmyM5OVkvvPCC6tSpo5IlS6p8+fJ64oknrLwWeJ+ftwsACuOll17SokWLNHPmTFWpUkVhYWFXdPwzZ85o3LhxkmT1AxdXxvHjxzVu3DhVqlRJ9erVu2T//fv3a9y4cWrRosVVEZo9tWXLFo0bN06PPPKIbrjhBm+X45Hly5frk08+0ZAhQ1S9enXt3r1bo0aN0vHjx/XZZ595uzxcYYQMFEvffvutqlSpogcffNDbpQDwwD333KNevXrJ19dXktS8eXMdPHhQ7777rjIyMuR0Or1cIa4kTpfgsqWnp2vUqFGKiYlRQECAypUrp4EDB7od/hwxYoRCQ0OVlZXlahs8eLAcDodeffVVV9vJkyfl4+OjN998M8+5Dh8+LIfDodWrV+vAgQNyOBxupyPGjRunRo0aKSwsTCEhIbr11ls1Y8YMXfg9gGvXrlWLFi0UHh6uoKAgVahQQffdd5/OnDmjw4cP68Ybb3SNlzPHI488ctHt8O9//1t33323SpQoodKlS6t///46ffp0nn1nzpypunXrKjAwUGFhYbr33nt14MCBi45/+PBh+fn5KTY2NtdzGzdulMPh0IIFC1xtmzdvVuvWrVWqVCmVKFFCTZo00bJly9zWGzt2rBwOR67xck6bXex0wrJly+RwOJSQkOBq+/TTT+VwONSpUye3vnXq1NF9993nWn777bd11113KSIiQsHBwbrllls0ceJEZWZmuq3XokUL1a5dWwkJCbrzzjtVokQJVa5cWa+88oqys7Ml/XlK6rbbbpMkPfroo679NXbs2Dzrnj17trp37y5Jatmypav/7NmzXX0Ks39ybN68WY0bN1ZgYKDKlSun559/XtOnT8+1PbOzszVx4kRVr15dTqdTERERevjhh3Xs2LGLjj927FiNGDFCkhQTE5Pr/0BBxzXGaOLEiapYsaICAwN16623asWKFbnmS09P11NPPaV69eopNDRUYWFhaty4ca6jDq1bt1b16tVz/V8zxuimm25yvSdKly7tChg5Dhw4oNDQUPn7+1/0taMYMsBfzJo1y0gy27ZtM5mZmbkeHTt2NBUrVnT1z87ONu3btzd+fn7m+eefN1988YX55z//aYKDg039+vVNenq6McaYlStXGklmy5YtrnWrV69ugoKCTNu2bV1t8+fPN5LM/v3786wvPT3dbN261dSvX99UrlzZbN261WzdutWkpKQYY4x55JFHzIwZM0x8fLyJj483L730kgkKCjLjxo1zjXHo0CETGBho2rZtaxYvXmzWr19v5s6da3r16mVOnTpl0tPTXfX27dvXNcfBgwfz3W5JSUkmIiLClCtXzsyaNcssX77cPPjgg6ZChQpGklm3bp2r7/jx440k88ADD5hly5aZ999/31SuXNmEhoaa77///qL759577zUVKlQw58+fd2vv3r27iYqKMpmZmcYYY9avX2/8/f1NgwYNzPz5883ixYtNu3btjMPhMPPmzXOtN2bMGJPXx0DO++DQoUP51nL69Gnj7+9vxo8f72rr37+/CQoKMsHBwebcuXPGGGN+/vln43A4zDvvvOPqN2zYMBMXF2dWrlxp1q5dayZNmmRKly5tHn30Ubc5mjdvbsLDw03VqlXNlClTTHx8vBkwYICRZN577z1jjDEpKSmuep977jnX/jp69GiedScnJ7v2wdtvv+3qn5ycbIy5vP3z9ddfm8DAQFOnTh0zb948s2TJEtOxY0dTqVKlXNuzX79+RpIZNGiQWblypZkyZYq58cYbTXR0tPnll1/ynePo0aNm8ODBRpJZuHBhrv8DBR03Z9/37dvXrFixwkydOtWUK1fOREZGmubNm7v6/f777+aRRx4xc+bMMWvXrjUrV640Tz/9tPHx8XHtA2OM+eyzz4wkEx8f71bvsmXLjCSzbNmyPF/PqFGj3PYnri2EDLjJ+bC+2OOvISPnh/HEiRPdxskJC1OnTjXGGJOWlmYCAgLMiy++aIwx5tixY0aSGTlypAkKCnKFkccee8xERUVdss7mzZubWrVqXbRPVlaWyczMNC+++KIJDw832dnZxhhjPvnkEyPJ7NmzJ991f/nlFyPJjBkz5pK1GGPMyJEjjcPhyDVm27Zt3ULGqVOnTFBQkOnYsaNbv8TERON0Ok3Pnj0vOs+6deuMJLNo0SJX208//WT8/PzcgtQdd9xhIiIizOnTp11t58+fN7Vr1zbly5d3bYvLCRnGGNOsWTPTqlUr1/JNN91kRowYYXx8fMyGDRuMMcbMnTvXSMr3B3TOfnr//feNr6+v+e2331zPNW/e3EgyX331lds6NWvWNO3bt3ctJyQkGElm1qxZF603x4IFC3KFP2Muf/90797dBAcHu/0wz8rKMjVr1nTbngcOHDCSzIABA9zW/+qrr4wk8+yzz150nldffTXP/VPQcU+dOmUCAwPNvffe69bvyy+/NJLcQsaFzp8/bzIzM03fvn1N/fr13V5n5cqVTZcuXdz6d+jQwVSpUsX1nvur0aNHG0lm0qRJF329KL44XYI8vf/++0pISMj1aNasmVu/tWvXSlKuUwndu3dXcHCw1qxZI0kqUaKEGjdurNWrV0uS4uPjdcMNN2jEiBE6d+6cNm/eLElavXq12rRpU+i6165dqzZt2ig0NFS+vr7y9/fXCy+8oJMnTyo5OVmSVK9ePQUEBKhfv35677339OOPPxZ6vhzr1q1TrVq1VLduXbf2nj17ui1v3bpVZ8+ezbW9oqOj1apVK9f2yk+LFi1Ut25dvf322662KVOmyOFwqF+/fpKktLQ0ffXVV+rWrZtKlizp6ufr66tevXrp2LFj+u677wrzMnNp3bq1vvzyS509e1ZHjhzRwYMHdf/996tevXqKj4+X9Oc+rVChgqpWrepab/fu3frb3/6m8PBw1356+OGHlZWVpe+//95tjsjISN1+++1ubXXq1NGRI0euyGv4q8vdPxs2bFCrVq1UunRpV5uPj4969Ojh1m/dunWScv+/uf3221WjRo1LzpOfgo67detWpaen57qmqUmTJqpYsWKucRcsWKCmTZuqZMmS8vPzk7+/v2bMmOF2CsnHx0eDBg3S559/rsTEREnSDz/8oJUrV2rAgAG5Tst99913Gj9+vIYNG6ahQ4cW6vXi6kfIQJ5q1Kihhg0b5nqEhoa69Tt58qT8/Pxc1zDkcDgcioyM1MmTJ11tbdq00bZt25SWlqbVq1erVatWCg8PV4MGDbR69WodOnRIhw4dKnTI2L59u9q1aydJmjZtmr788kslJCRo9OjRkqSzZ89KkqpUqaLVq1crIiJCAwcOVJUqVVSlShVNnjy5UPNKf26HyMjIXO0XtuVsj7Jly+bqGxUV5ba98vPkk09qzZo1+u6775SZmalp06apW7durrlOnTolY0y+c/y1jsvVpk0bZWRkaPPmzYqPj1fp0qVVv359tWnTxhUo16xZ47ZPExMTdeedd+qnn37S5MmTtWnTJiUkJLiCU85+yhEeHp5rXqfTmavflXC5++fkyZMqU6ZMrvYL267E+yC/+Qsybs6/BXnPLly4UD169FC5cuX0wQcfaOvWrUpISFCfPn2Unp7u1rdPnz4KCgrSlClTJP157U1QUJD69OmTa54DBw7IGJPr+h1cWwgZuCzh4eE6f/68fvnlF7d2Y4ySkpLcfqNr3bq1zp07p40bN2rNmjVq27atqz0+Pt71m2/r1q0LVcu8efPk7++vzz//XD169FCTJk3UsGHDPPveeeedWrp0qVJSUrRt2zY1btxYQ4cO1bx58wo1d3h4uJKSknK1X9iW8wPzxIkTufoeP37cbXvlp2fPngoPD9fbb7+tBQsWKCkpSQMHDnQ9/1//9V/y8fHJdw5JrnkCAwMlSRkZGW79fv3110vWIUmNGjVSyZIltXr1asXHx6t169ZyOBxq3bq16+hXYmKiW8hYvHix0tLStHDhQj300ENq1qyZGjZsqICAgALNadPl7p/w8HD9/PPPudptvA/ym78g4+b0K8h79oMPPlBMTIzmz5+vrl276o477lDDhg1zvWckKTQ0VL1799b06dP122+/adasWerZs2eet9mWKFFC1apVU6lSpTx+nSg+CBm4LDmB4IMPPnBr//TTT5WWluYWGG6//XaFhITojTfeUFJSkitktGnTRrt379bHH3+smjVrun7b9pTD4ZCfn5/bletnz57VnDlz8l3H19dXjRo1cv0WvWvXLkly3UZX0N+WW7ZsqX379unrr792a//www/dlhs3bqygoKBc2+vYsWNau3ZtgQJWYGCg61TP66+/rnr16qlp06au54ODg9WoUSMtXLjQrf7s7Gx98MEHKl++vG6++WZJcv2NiG+++cZtjqVLl176RUvy9/fXXXfdpfj4eK1du9a1T++88075+fnpueeec4WOHDmHzf96q6IxRtOmTSvQnHnxdH/l1/9y90/z5s21du1at5CWnZ3tdtePJLVq1UpS7v83CQkJOnDgwCXnya/+go57xx13KDAwUHPnznXrt2XLllynoRwOhwICAtxOdyQlJeX7Ny2efPJJ/frrr+rWrZt+//13DRo0KM9+7dq107///e9cp8JwjfHuJSG42uRc8JeQkJDn8506dcrz7hJ/f38zduxYEx8fb1577TVTsmRJt7tLcnTu3NlIMjExMa629PR0ExQUZCSZJ598skB15nXh55o1a4wk061bN/PFF1+Yjz76yDRo0MBUrVrV7SK5uLg40717dzN79myzdu1as3z5ctOtWzcjyaxatco1XsWKFU21atXMqlWrTEJCwkUvgjxx4oS58cYbc91dEh0dne/dJb169TLLly83c+bMMTfddFOB7l7IcezYMePn52ckmenTp+d6PufukkaNGpkFCxaYzz77zLRv3z7X3SUpKSkmLCzM3HLLLWbRokVm6dKl5r777jMxMTEFuvDTGGNee+0110XBhw8fdrW3bNnSSDJ16tRx63/gwAETEBBgWrRoYZYvX24WLlxo2rZt69pPf91W+V3g27t3b7f3YVpamgkKCjJNmzY169atMwkJCeann37Kt+Yff/zRSDJdu3Y1mzZtMgkJCebXX381xlze/tmzZ4/r7pL58+e77i6pWLGikWSOHDni6tuvXz/jcDjM0KFDzapVq8y7775rIiIiTHR0tKuW/ORcAPz444+bLVu2mISEBJOamurRuM8995zr7pKVK1eaadOm5Xl3ycyZM40k88QTT5g1a9aY2bNnmypVqrj2V146dOhgJJlmzZrl+xree+894+vra9avX3/R14rijZABN56GDGOMOXv2rBk5cqSpWLGi8ff3N2XLljVPPPGEOXXqVK71J0+ebCSZxx57zK095y6MJUuWFKjO/H74zJw501SrVs04nU5TuXJlExsba2bMmOH2A3Pr1q3m3nvvNRUrVjROp9OEh4eb5s2b55p79erVpn79+sbpdBpJpnfv3hetaf/+/aZt27YmMDDQhIWFmb59+7pu67vwLobp06ebOnXqmICAABMaGmq6dOli9u3bV6DXnqNFixYmLCzMnDlzJs/nN23aZFq1amWCg4NNUFCQueOOO8zSpUtz9du+fbtp0qSJCQ4ONuXKlTNjxowx06dPL3DI+Prrr40kU7VqVbf2l19+2Ugyw4cPz7XO0qVLTd26dU1gYKApV66cGTFihFmxYkWhQ4Yxxnz00UemevXqxt/fv0B3Br3xxhsmJibG+Pr65roz5XL2z6ZNm0yjRo2M0+k0kZGRZsSIEWbChAlGkvn9999d/bKyssyECRPMzTffbPz9/U3p0qXNQw89lO+ttxcaNWqUiYqKMj4+Pm7braDjZmdnm9jYWBMdHW0CAgJMnTp1zNKlS03z5s1z3V3yyiuvmEqVKhmn02lq1Khhpk2blu+dScYYM3v2bCPJLdBeKOez5sL/G7i2OIy54C+nALjqJScnq2LFiho8eLAmTpzo7XJwCe3atdPhw4dz3Tlzrbrvvvu0bds2HT58mD+wdZ3jz4oDxcixY8f0448/6tVXX5WPj4+GDBni7ZJwgeHDh6t+/fqKjo7Wb7/9prlz5yo+Pl4zZszwdmlWZWRkaNeuXdq+fbsWLVqk119/nYABQgZQnEyfPl0vvviiKlWqpLlz56pcuXLeLgkXyMrK0gsvvKCkpCQ5HA7VrFlTc+bM0UMPPeTt0qw6ceKEmjRpopCQED3++OMaPHiwt0vCVYDTJQAAwApuYQUAAFYQMgAAgBWEDAAAYEWRX/iZnZ2t48ePq1SpUrm+MAcAAFydjDE6ffq0oqKi5ONTsGMURR4yjh8/rujo6KKeFgAAXAFHjx5V+fLlC9S3yENGzpfhHD16VCEhIUU9PQAAKITU1FRFR0d79KV2RR4yck6RhISEEDIAAChmPLnUgQs/AQCAFYQMAABgBSEDAABYQcgAAABWEDIAAIAVhAwAAGAFIQMAAFhByAAAAFYQMgAAgBWEDAAAYAUhAwAAWEHIAAAAVhAyAACAFYQMAABgRZF/1XuO2mNWycdZwlvTA8B15fArnbxdAq5DHMkAAABWEDIAAIAVhAwAAGAFIQMAAFhByAAAAFYQMgAAgBWEDAAAYAUhAwAAWEHIAAAAVhAyAACAFYQMAABgBSEDAABYQcgAAABWEDIAAIAVhAwAAGAFIQMAAFhByAAAAFYQMgAAgBWEDAAAYAUhAwAAWEHIAAAAVhAyAACAFYQMAABgRaFCxjvvvKOYmBgFBgaqQYMG2rRp05WuCwAAFHMeh4z58+dr6NChGj16tHbv3q0777xTHTp0UGJioo36AABAMeUwxhhPVmjUqJFuvfVWxcXFudpq1Kihrl27KjY29pLrp6amKjQ0VNFDP5aPs4TnFQPANSb7XLr1OQ68dLfV8YODg62OD+/L+fmdkpKikJCQAq3j58kE586d086dO/XMM8+4tbdr105btmzJc52MjAxlZGS4FQkA+H9HJ3WzPkfJSXbH9/D3VVwnPDpd8uuvvyorK0tlypRxay9TpoySkpLyXCc2NlahoaGuR3R0dOGrBQAAxYZHRzJyOBwOt2VjTK62HKNGjdLw4cNdy6mpqQQNAPiL6GGfWJ/D9ukSIC8ehYzSpUvL19c311GL5OTkXEc3cjidTjmdzsJXCADXOJ+AQOtzcM0EvMGj0yUBAQFq0KCB4uPj3drj4+PVpEmTK1oYAAAo3jw+XTJ8+HD16tVLDRs2VOPGjTV16lQlJiaqf//+NuoDAADFlMch43/+53908uRJvfjiizpx4oRq166t5cuXq2LFijbqAwAAxVShLvwcMGCABgwYcKVrAQAA1xC+uwQAAFhByAAAAFYQMgAAgBWEDAAAYAUhAwAAWEHIAAAAVhAyAACAFYQMAABgBSEDAABYQcgAAABWEDIAAIAVhAwAAGAFIQMAAFhByAAAAFYQMgAAgBWEDAAAYAUhAwAAWEHIAAAAVhAyAACAFYQMAABgBSEDAABY4eetib8d114hISHemh4AAFjGkQwAAGAFIQMAAFhByAAAAFYQMgAAgBWEDAAAYAUhAwAAWEHIAAAAVhAyAACAFYQMAABgBSEDAABYQcgAAABWEDIAAIAVhAwAAGCF176FtfaYVfJxlvDW9ACAAjr8Sidvl4BiiiMZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArPA4ZGzcuFGdO3dWVFSUHA6HFi9ebKEsAABQ3HkcMtLS0lS3bl299dZbNuoBAADXCD9PV+jQoYM6dOhgoxYAQBHLPpd+yT5paWkXfT44OPhKlYNrjMchw1MZGRnKyMhwLaemptqeEgBQQEcndbtkn5KTLv68MeYKVYNrjfULP2NjYxUaGup6REdH254SAABcBawfyRg1apSGDx/uWk5NTSVoAMBVInrYJ5fsc+Clu4ugElyLrIcMp9Mpp9NpexoAQCH4BAResg/XXKCw+DsZAADACo+PZPzxxx86ePCga/nQoUPas2ePwsLCVKFChStaHAAAKL48Dhk7duxQy5YtXcs511v07t1bs2fPvmKFAQCA4s3jkNGiRQtuVwIAAJfENRkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACj9vTfztuPYKCQnx1vQAAMAyjmQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACkIGAACwgpABAACsIGQAAAArvPYtrLXHrJKPs4S3pgeA69bhVzp5uwRcJziSAQAArCBkAAAAKwgZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwAqPQkZsbKxuu+02lSpVShEREeratau+++47W7UBAIBizKOQsWHDBg0cOFDbtm1TfHy8zp8/r3bt2iktLc1WfQAAoJjy86TzypUr3ZZnzZqliIgI7dy5U3fdddcVLQwAcHmyz6Xn2Z7XL4bBwcG2y8F1yKOQcaGUlBRJUlhYWL59MjIylJGR4VpOTU29nCkBAAV0dFK3PNtLTsrdZoyxXA2uR4W+8NMYo+HDh6tZs2aqXbt2vv1iY2MVGhrqekRHRxd2SgAAUIw4TCHj68CBA7Vs2TJt3rxZ5cuXz7dfXkcyoqOjFT30Y/k4SxRmagBAAeR3uuTAS3fnauN0CS4lNTVVoaGhSklJUUhISIHWKdTpksGDB2vJkiXauHHjRQOGJDmdTjmdzsJMAwC4DD4BgXm2EyhQVDwKGcYYDR48WIsWLdL69esVExNjqy4AAFDMeRQyBg4cqA8//FCfffaZSpUqpaSkJElSaGiogoKCrBQIAACKJ48u/IyLi1NKSopatGihsmXLuh7z58+3VR8AACimPD5dAgAAUBB8dwkAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACkIGAACwws9bE387rr1CQkK8NT0AALCMIxkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKr30La+0xq+TjLOGt6QHAY4df6eTtEoBihSMZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArCBkAAAAKwgZAADACkIGAACwgpABAACsIGQAAAArCBkAAMAKQgYAALCCkAEAAKwgZAAAACsIGQAAwApCBgAAsIKQAQAArPAoZMTFxalOnToKCQlRSEiIGjdurBUrVtiqDQAAFGMehYzy5cvrlVde0Y4dO7Rjxw61atVKXbp00b59+2zVBwAAiik/Tzp37tzZbfnll19WXFyctm3bplq1al3RwgDAU9nn0q2On5aWZnX84OBgq+MDRc2jkPFXWVlZWrBggdLS0tS4ceN8+2VkZCgjI8O1nJqaWtgpAeCijk7qZnX8kpOsDi9jjN0JgCLm8YWfe/fuVcmSJeV0OtW/f38tWrRINWvWzLd/bGysQkNDXY/o6OjLKhgAABQPDuNhdD537pwSExP1+++/69NPP9X06dO1YcOGfINGXkcyoqOjFT30Y/k4S1xe9QDwF7ZPlxx46W6r43O6BFez1NRUhYaGKiUlRSEhIQVax+PTJQEBAbrpppskSQ0bNlRCQoImT56sd999N8/+TqdTTqfT02kAwGM+AYFWxycEAJ657L+TYYxxO1IBAAAgeXgk49lnn1WHDh0UHR2t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qeeee06zZs1SXFyc9u3bp2HDhumhhx7Shg0b3Pr//e9/V2xsrA4cOKB27dpp9erVkqTt27frxIkTio6O1qJFizRkyBA99dRT+vbbb/X444/r0Ucf1bp169zGGjNmjLp06aK9e/e6vu30hx9+0IoVK7Ry5Up99NFHmjlzpjp16qRjx45pw4YNmjBhgp577jlt27bNNY6Pj4/+9a9/6dtvv9V7772ntWvX6u9//7vbXGfOnNE///lPzZkzRxs3blRiYqKefvpp1/NxcXEaOHCg+vXrp71792rJkiW66aabJP151KZTp05KSkrS8uXLtXPnTt16661q3bq1fvvttyu3MwDkrdBf4QagyPTu3dt06dLFtfzVV1+Z8PBw061bNxMYGGi2bNni1r9v377mgQceMMb8/7ctLl682K3P7t27jSRz6NAhV1uTJk3MY4895tave/fupmPHjq5lSWbo0KFufcaMGWNKlChhUlNTXW3t27c3lSpVMllZWa62atWqmdjY2Hxf58cff2zCw8Ndy7NmzTKSzMGDB11tb7/9tilTpoxrOSoqyowePTrP8dasWWNCQkJMenq6W3uVKlXMu+++m28dAK4MrskAionPP/9cJUuW1Pnz55WZmakuXbro6aef1ieffKK2bdu69T137pzq16/v1tawYcNLznHgwIFcF4A2bdpUkydPvuRYlSpVUqlSpVzLZcqUka+vr3x8fNzakpOTXcvr1q3T+PHjtX//fqWmpur8+fNKT09XWlqagoODJUklSpRQlSpVXOuULVvWNUZycrKOHz+u1q1b5/l6du7cqT/++EPh4eFu7WfPntUPP/xw0W0B4PIRMoBiomXLloqLi5O/v7+ioqLk7++vr776SpK0bNkylStXzq2/0+l0W875oX0pDofDbdkYk6str7EuvPgz506YC9uys7MlSUeOHFHHjh3Vv39/vfTSSwoLC9PmzZvVt29fZWZmXnRcY4ykP08ZXUx2drbKli2r9evX53ruhhtuuOi6AC4fIQMoJoKDg13XGuSoWbOmnE6nEhMT1bx588ueo0aNGtq8ebMefvhhV9uWLVtUo0aNyx77Qjt27ND58+f12muvuY52fPzxxx6NUapUKVWqVElr1qxRy5Ytcz1/6623KikpSX5+fqpUqdKVKBuABwgZQDFWqlQpPf300xo2bJiys7PVrFkzpaamasuWLSpZsqR69+7t0XgjRoxQjx49XBdHLl26VAsXLnRdJHolValSRefPn9ebb76pzp0768svv9SUKVM8Hmfs2LHq37+/IiIi1KFDB50+fVpffvmlBg8erDZt2qhx48bq2rWrJkyYoGrVqun48eNavny5unbtWqBTSAAKj7tLgGLupZde0gsvvKDY2FjVqFFD7du319KlSxUTE+PxWF27dtXkyZP16quvqlatWnr33Xc1a9YstWjR4orXXa9ePb3++uuaMGGCateurblz5yo2NtbjcXr37q033nhD77zzjmrVqqV77rlH//nPfyT9eWpl+fLluuuuu9SnTx/dfPPNuv/++3X48GGVKVPmSr8kABdwmJyTmwAAAFcQRzIAAIAVhAwAAGAFIQMAAFhByAAAAFYQMgAAgBWEDAAAYAUhAwAAWEHIAAAAVhAyAACAFYQMAABgBSEDAABYQcgAAABW/B+NuquDF01zQAAAAABJRU5ErkJggg== )]

直方图

In [122]:

import matplotlib
import numpy as np
import matplotlib.pyplot as pltnp.random.seed(19680801)# example data
mu = 100  # mean of distribution
sigma = 15  # standard deviation of distribution
x = mu + sigma * np.random.randn(437)num_bins = 50fig, ax = plt.subplots()# the histogram of the data
n, bins, patches = ax.hist(x, num_bins, density=1)# add a 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
ax.plot(bins, y, '--')ax.set_xlabel('Smarts')
ax.set_ylabel('Probability density')
ax.set_title(r'Histogram of IQ: $\\mu=100$, $\\sigma=15$')# Tweak spacing to prevent clipping of ylabel
fig.tight_layout()
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uw97KDlJ-1681776922771)(data:image/png;base64,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 )]

绘制日期图像

  • 从csv或者excel中读取数据,先用pandas转化一下格式 x = pd.to_datetime(df_stock_data_1[‘Date’]
  • 格式转换器 mdates.DateFormatter(‘%Y-%m-%d’)
  • 设置定位符 ax.xaxis.set_major_locator(years) ax.xaxis.set_major_formatter(yearsFmt) ax.xaxis.set_minor_locator(months)

In [124]:

# ignore warning
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbookyears = mdates.YearLocator()   # every year
months = mdates.MonthLocator()  # every month
yearsFmt = mdates.DateFormatter('%Y')# Load a numpy record array from yahoo csv data with fields date, open, close,
# volume, adj_close from the mpl-data/example directory. The record array
# stores the date as an np.datetime64 with a day unit ('D') in the date column.
# with cbook.get_sample_data('goog.npz') as datafile:
#     r = np.load(datafile)['price_data'].view(np.recarray)  获取日期数据fig, ax = plt.subplots()
ax.plot(r.date, r.adj_close)# format the ticks  定位日期
ax.xaxis.set_major_locator(years)
ax.xaxis.set_major_formatter(yearsFmt)
ax.xaxis.set_minor_locator(months)# round to nearest years...
datemin = np.datetime64(r.date[0], 'Y')
datemax = np.datetime64(r.date[-1], 'Y') + np.timedelta64(1, 'Y')
ax.set_xlim(datemin, datemax)# format the coords message box
def price(x):return '$%1.2f' % xax.format_xdata = mdates.DateFormatter('%Y-%m-%d')   
ax.format_ydata = price
ax.grid(True)# rotates and right aligns the x labels, and moves the bottom of the
# axes up to make room for them
fig.autofmt_xdate()plt.show()
<ipython-input-124-23ffbb25a115>:14: MatplotlibDeprecationWarning: In a future release, get_sample_data will automatically load numpy arrays.  Set np_load to True to get the array and suppress this warning.  Set asfileobj to False to get the path to the data file and suppress this warning.with cbook.get_sample_data('goog.npz') as datafile:
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
<ipython-input-124-23ffbb25a115> in <module>12 # volume, adj_close from the mpl-data/example directory. The record array13 # stores the date as an np.datetime64 with a day unit ('D') in the date column.
---> 14 with cbook.get_sample_data('goog.npz') as datafile:15     r = np.load(datafile)['price_data'].view(np.recarray)16 D:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook\\__init__.py in get_sample_data(fname, asfileobj, np_load)463                     "asfileobj to False to get the path to the data file and "464                     "suppress this warning.")
--> 465                 return path.open('rb')466         elif suffix in ['.csv', '.xrc', '.txt']:467             return path.open('r')D:\\ProgramData\\Anaconda3\\lib\\pathlib.py in open(self, mode, buffering, encoding, errors, newline)1219         if self._closed:1220             self._raise_closed()
-> 1221         return io.open(self, mode, buffering, encoding, errors, newline,1222                        opener=self._opener)1223 D:\\ProgramData\\Anaconda3\\lib\\pathlib.py in _opener(self, name, flags, mode)1075     def _opener(self, name, flags, mode=0o666):1076         # A stub for the opener argument to built-in open()
-> 1077         return self._accessor.open(self, flags, mode)1078 1079     def _raw_open(self, flags, mode=0o777):FileNotFoundError: [Errno 2] No such file or directory: 'D:\\\\ProgramData\\\\Anaconda3\\\\lib\\\\site-packages\\\\matplotlib\\\\mpl-data\\\\sample_data\\\\goog.npz'

自定义时间或者 自动匹配

格式转换函数 def format_date(x, pos=None): thisind = np.clip(int(x + 0.5), 0, N - 1) return date[thisind].strftime(‘%Y-%m-%d’)

自定义匹配 ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))

In [ ]:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.ticker as ticker# Load a numpy record array from yahoo csv data with fields date, open, close,
# volume, adj_close from the mpl-data/example directory. The record array
# stores the date as an np.datetime64 with a day unit ('D') in the date column.
with cbook.get_sample_data('goog.npz') as datafile:r = np.load(datafile)['price_data'].view(np.recarray)
r = r[-30:]  # get the last 30 days
# Matplotlib works better with datetime.datetime than np.datetime64, but the
# latter is more portable.
date = r.date.astype('O')# first we'll do it the default way, with gaps on weekends
fig, axes = plt.subplots(ncols=2, figsize=(8, 4))
ax = axes[0]
ax.plot(date, r.adj_close, 'o-')
ax.set_title("Default")
fig.autofmt_xdate()# next we'll write a custom formatter
N = len(r)
ind = np.arange(N)  # the evenly spaced plot indicesdef format_date(x, pos=None):thisind = np.clip(int(x + 0.5), 0, N - 1)return date[thisind].strftime('%Y-%m-%d')ax = axes[1]
ax.plot(ind, r.adj_close, 'o-')
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
ax.set_title("Custom tick formatter")
fig.autofmt_xdate()plt.show()

In [ ]:


绘制分类变量

In [18]:

import matplotlib.pyplot as pltdata = {'apples': 10, 'oranges': 15, 'lemons': 5, 'limes': 20}
names = list(data.keys())
values = list(data.values())fig, axs = plt.subplots(1, 3, figsize=(9, 3), sharey=True)  #subplot(几行,几列,图像大小(与行列要匹配))
axs[0].bar(names, values)
axs[1].scatter(names, values)
axs[2].plot(names, values)
fig.suptitle('Categorical Plotting')

Out[18]:

Text(0.5, 0.98, 'Categorical Plotting')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RewEKlqj-1681776922773)(data:image/png;base64,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 )]

In [19]:

cat = ["bored", "happy", "bored", "bored", "happy", "bored"]
dog = ["happy", "happy", "happy", "happy", "bored", "bored"]
activity = ["combing", "drinking", "feeding", "napping", "playing", "washing"]fig, ax = plt.subplots()
ax.plot(activity, dog, label="dog")
ax.plot(activity, cat, label="cat")
ax.legend()plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BlFiGTdS-1681776922774)(data:image/png;base64,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 )]

绘制两曲线的相干性

In [22]:

import numpy as np
import matplotlib.pyplot as plt# Fixing random state for reproducibility
np.random.seed(19680801)dt = 0.01
t = np.arange(0, 30, dt)
nse1 = np.random.randn(len(t))                 # white noise 1
nse2 = np.random.randn(len(t))                 # white noise 2# Two signals with a coherent part at 10Hz and a random part
s1 = np.sin(2 * np.pi * 10 * t) + nse1
s2 = np.sin(2 * np.pi * 10 * t) + nse2fig, axs = plt.subplots(2, 1)
axs[0].plot(t, s1, t, s2)
axs[0].set_xlim(0, 2)
axs[0].set_xlabel('time')
axs[0].set_ylabel('s1 and s2')
axs[0].grid(True)cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt)   # 俩曲线的相干度曲线
axs[1].set_ylabel('coherence')fig.tight_layout()
plt.show()
15     r = np.load(datafile)['price_data'].view(np.recarray)16 

D:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\cbook_init_.py in get_sample_data(fname, asfileobj, np_load)
463 "asfileobj to False to get the path to the data file and "
464 “suppress this warning.”)
–> 465 return path.open(‘rb’)
466 elif suffix in [‘.csv’, ‘.xrc’, ‘.txt’]:
467 return path.open(‘r’)

D:\\ProgramData\\Anaconda3\\lib\\pathlib.py in open(self, mode, buffering, encoding, errors, newline)
1219 if self._closed:
1220 self._raise_closed()
-> 1221 return io.open(self, mode, buffering, encoding, errors, newline,
1222 opener=self._opener)
1223

D:\\ProgramData\\Anaconda3\\lib\\pathlib.py in _opener(self, name, flags, mode)
1075 def _opener(self, name, flags, mode=0o666):
1076 # A stub for the opener argument to built-in open()
-> 1077 return self._accessor.open(self, flags, mode)
1078
1079 def _raw_open(self, flags, mode=0o777):

FileNotFoundError: [Errno 2] No such file or directory: ‘D:\\ProgramData\\Anaconda3\\lib\\site-packages\\matplotlib\\mpl-data\\sample_data\\goog.npz’


#### 自定义时间或者 自动匹配格式转换函数 def format_date(x, pos=None): thisind = np.clip(int(x + 0.5), 0, N - 1) return date[thisind].strftime('%Y-%m-%d')自定义匹配 ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))In [ ]:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cbook as cbook
import matplotlib.ticker as ticker

Load a numpy record array from yahoo csv data with fields date, open, close,

volume, adj_close from the mpl-data/example directory. The record array

stores the date as an np.datetime64 with a day unit (‘D’) in the date column.

with cbook.get_sample_data(‘goog.npz’) as datafile:
r = np.load(datafile)[‘price_data’].view(np.recarray)
r = r[-30:] # get the last 30 days

Matplotlib works better with datetime.datetime than np.datetime64, but the

latter is more portable.

date = r.date.astype(‘O’)

first we’ll do it the default way, with gaps on weekends

fig, axes = plt.subplots(ncols=2, figsize=(8, 4))
ax = axes[0]
ax.plot(date, r.adj_close, ‘o-’)
ax.set_title(“Default”)
fig.autofmt_xdate()

next we’ll write a custom formatter

N = len®
ind = np.arange(N) # the evenly spaced plot indices

def format_date(x, pos=None):
thisind = np.clip(int(x + 0.5), 0, N - 1)
return date[thisind].strftime(‘%Y-%m-%d’)

ax = axes[1]
ax.plot(ind, r.adj_close, ‘o-’)
ax.xaxis.set_major_formatter(ticker.FuncFormatter(format_date))
ax.set_title(“Custom tick formatter”)
fig.autofmt_xdate()

plt.show()


In [ ]:

## 绘制分类变量In [18]:

import matplotlib.pyplot as plt

data = {‘apples’: 10, ‘oranges’: 15, ‘lemons’: 5, ‘limes’: 20}
names = list(data.keys())
values = list(data.values())

fig, axs = plt.subplots(1, 3, figsize=(9, 3), sharey=True) #subplot(几行,几列,图像大小(与行列要匹配))
axs[0].bar(names, values)
axs[1].scatter(names, values)
axs[2].plot(names, values)
fig.suptitle(‘Categorical Plotting’)


Out[18]:

Text(0.5, 0.98, ‘Categorical Plotting’)


[外链图片转存中...(img-RewEKlqj-1681776922773)]In [19]:

cat = [“bored”, “happy”, “bored”, “bored”, “happy”, “bored”]
dog = [“happy”, “happy”, “happy”, “happy”, “bored”, “bored”]
activity = [“combing”, “drinking”, “feeding”, “napping”, “playing”, “washing”]

fig, ax = plt.subplots()
ax.plot(activity, dog, label=“dog”)
ax.plot(activity, cat, label=“cat”)
ax.legend()

plt.show()


[外链图片转存中...(img-BlFiGTdS-1681776922774)]## 绘制两曲线的相干性In [22]:

import numpy as np
import matplotlib.pyplot as plt

Fixing random state for reproducibility

np.random.seed(19680801)

dt = 0.01
t = np.arange(0, 30, dt)
nse1 = np.random.randn(len(t)) # white noise 1
nse2 = np.random.randn(len(t)) # white noise 2

Two signals with a coherent part at 10Hz and a random part

s1 = np.sin(2 * np.pi * 10 * t) + nse1
s2 = np.sin(2 * np.pi * 10 * t) + nse2

fig, axs = plt.subplots(2, 1)
axs[0].plot(t, s1, t, s2)
axs[0].set_xlim(0, 2)
axs[0].set_xlabel(‘time’)
axs[0].set_ylabel(‘s1 and s2’)
axs[0].grid(True)

cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) # 俩曲线的相干度曲线
axs[1].set_ylabel(‘coherence’)

fig.tight_layout()
plt.show()


![img](data:image/png;base64,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