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是纵向分割
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)
提取原数据:用
.array
或to_numpy
提取数据排除缺失值:一般的聚合函数都有
skipna
关键字,指定是否要排除缺失数据,默认值为True
。
- 注意:NumPy 的
mean
、std
、sum
等方法默认不统计 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()
函数计算最大值与最小值对应的索引。
idxmin
与 idxmax
对应 NumPy 里的 argmin
与 argmax
apply()
方法沿着 DataFrame 的轴应用函数,比如,描述性统计方法,该方法支持axis
参数。它就是对DataFrame的行或列进行操作
In [141]: df.apply(np.mean)
Out[141]:
one 0.811094
two 1.360588
three 0.187958
dtype: float64
In [142]: df.apply(np.mean, axis=1)
Out[142]:
a 1.583749
b 0.734929
c 1.133683
d -0.166914
dtype: float64
In [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: float64
In [147]: df.apply('mean', axis=1)
Out[147]:
a 1.583749
b 0.734929
c 1.133683
d -0.166914
dtype: float64
就是组合多个函数
tsdf.agg(['sum', 'mean'])
Out[160]:
A B C
sum 3.033606 -1.803879 1.575510
mean 0.505601 -0.300647 0.262585
In [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()
支持 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(data=[],index=[])
import pandas as pd
from pandas import Series
s= 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是二维的,自动生成行列的索引
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是对应的函数
唯一值:
对序列使用
unique
和nunique
可以分别得到其唯一值组成的列表和唯一值的个数:
df['School'].unique()
df['School'].nunique()
去重的使用:
duplicated
和drop_duplicates
的功能类似,但前者返回了是否为唯一值的布尔列表,其keep
参数与后者一致。其返回的序列,把重复元素设为True
,否则为False
。drop_duplicates
等价于把duplicated
为True
的对应行剔除。
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
逻辑替换:
包括了
where
和mask
,这两个函数是完全对称的: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[*]
,这个*
是指行的筛选。其中,*
的位置一共有五类合法对象,分别是:单个元素、元素列表、元素切片、布尔列表以及函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 > 70
condition_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 > 80
condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3
df_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 > 70
condition_1 = condition_1_1 & condition_1_2 & condition_1_3
condition_2_1 = x.School == 'Peking University'
condition_2_2 = x.Grade == 'Senior'
condition_2_3 = x.Weight > 80
condition_2 = condition_2_1 & (~condition_2_2) & condition_2_3
result = condition_1 | condition_2
return result
df_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')]
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) )
缺失数据可以使用
isna
或isnull
(两个函数没有区别)来查看每个单元格是否缺失,结合mean
可以计算出每列缺失值的比例:df.isnull()/isna()
如果想要查看某一列缺失或者非缺失的行,可以利用
Series
上的isna
或者notna
进行布尔索引。例如,查看身高缺失的行:
df[df.Height.isna()]
如果想要同时对几个列,检索出全部为缺失或者至少有一个缺失或者没有缺失的行,可以使用
isna, notna
和any, 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
的主要参数为轴方向axis
(默认为0,即删除行)、删除方式how
、删除的非缺失值个数阈值thresh
(非缺失值非缺失值没有达到这个数量的相应维度会被删除)、备选的删除子集subset
,其中how
主要有any
和all
两种参数可以选择。
例如,删除身高体重至少有一个缺失的行:
res = df.dropna(how = 'any', subset = ['Height', 'Weight'])
res.shape
例如,删除超过15个缺失值的列:
res = df.dropna(1, thresh=df.shape[0]-15) # 身高被删除
res.head()
在
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
,这与fillna
的method
中的ffill
是类似的,若想要后向限制插值或者双向限制插值可以指定为backward
或both
。
默认线性插值法下分别进行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
类型中存储缺失值,都会转为pandas
内置的pd.NA
:
一般在实际数据处理时,可以在数据集读入后,先通过convert_dtypes
转为Nullable
类型:
df = pd.read_csv('../data/learn_pandas.csv')
df = df.convert_dtypes()
df.dtypes
缺失数据的计算:
当调用函数sum, prod
使用加法和乘法的时候,缺失数据等价于被分别视作0和1,即不改变原来的计算结果:
当使用累计函数时,会自动跳过缺失值所处的位置:
另外需要注意的是,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')
在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()
此外,在字符串中出现与列表的比较时,==
和!=
分别表示元素出现在列表和没有出现在列表,等价于in
和not 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%的样本)。
replace
和weights
分别是指是否放回和每个样本的抽样相对概率,当replace = True
则表示有放回抽样。例如,对下面构造的df_sample
以value
值的相对大小为抽样概率进行有放回抽样,抽样数量为3。
df_sample.sample(3, replace = True, weights = df_sample.value)
loc:
由于多级索引中的单个元素以元组为单位,因此之前在第一节介绍的
loc
和iloc
方法完全可以照搬,只需把标量的位置替换成对应的元组。当传入元组列表或单个元组或返回前二者的函数时,需要先进行索引排序以避免性能警告:
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
对象就能解决这个问题。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)]
交换:
索引层的交换由swaplevel
和reorder_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_index
是set_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 10
2 2 20
b 1 3 30
2 4 40
c 1 5 50
2 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')
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 datetime
In [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)
groupby后会生成一个groupby对象,该对象不会返回任何内容
从上述的几个例子中不难看出,想要实现分组操作,必须明确三个要素:
分组依据、数据来源、操作及其返回结果。
同时从充分性的角度来说,如果明确了这三方面,就能确定一个分组操作,从而分组代码的一般模式即:
df.groupby(分组依据)[数据来源].使用操作
例如第一个例子中的代码就应该如下:
df.groupby('Gender')['Longevity'].mean()
基本内容:
- 根据某一列分组
- 根据某几列分组
- 组容量与组数
- 组的遍历
- level参数(多级索引)和axis参数
分组完想df可视化 : to_frame()
“group by” 指的是涵盖下列一项或多项步骤的处理流程:
分组后取出一个分组:
df_group = df.groupby('...')
df_group.get_group(分组名)
根据几组分:df.groupby(['...'])
查看组的容量: group.size 返回的是表长乘以表宽的大小,但在groupby对象上表示统计每个组的元素个数
查看组数:group.ngroups
groups属性是获取分组的字典: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 = 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()
虽然在
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']/100
Weight = x['Weight']
BMI_value = Weight/Height**2
return 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']*20
df.groupby(condition)['Price'].mean()
res = df.groupby('Type').agg({'Price': ['max'], 'HP': ['min']})
# res.columns = res.columns.map(lambda x:'_'.join(x))
res.columns
res = df.groupby('Type')['Price','HP'].agg(['max','min'])
# res.columns = res,columns.map(lambda x:'_'.join(x))
res
apply函数会遍历指定的所有行或者列
标量返回
df[['school','Math','Height']].groupby('school').apply(lambda x:x.max())
列表返回
df[['school','Math','Height']].groupby('school') .apply(lambda x: pd.DataFrame({"col":values}))
数据框返回
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
。需要说明的是,以日期偏置为窗口大小的滑动窗口将在第十章讨论,指数加权窗口见本章练习。
要使用滑窗函数,就必须先要对一个序列使用.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
,默认为1shift表示取向前第
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)]
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} + …
对于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
个元素作为窗口进行滑动加权平滑。请根据滑窗函数,给出新的wi
与yt
的更新公式,并通过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)]
pviot表具有三个参数,index,columns,values,
pivot
相关的三个参数允许被设置为列表,这也意味着会返回多级索引 .利用
pivot
进行变形操作需要满足唯一性的要求,即由于在新表中的行列索引对应了唯一的value
,因此原表中的index
和columns
对应两个列的行组合必须唯一。
将此表进行变形:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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表的基础上进行统计计算或边缘合并,就得用到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表就是和pivot表的互逆转化
df_melted = df.melt(id_vars = ['Class', 'Name'], #行索引
value_vars = ['Chinese', 'Math'], # 将这俩个列索引变成列的值
var_name = 'Subject', # 上面变成列的列名字
value_name = 'Grade') #变化后的列索引名
df_melted
现在如果列中包含了交叉类别,比如期中期末的类别和语文数学的类别,那么想要把
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([‘往后移的列(想往前移的列的前面的列’])
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_on
和right_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
,除了必须的on
和how
之外,可以对重复的列指定左右后缀
lsuffix
和rsuffix
。其中,
on
参数指索引名,单层索引时省略参数表示按照当前索引连接。df1.join(df2, how='left') df1.join(df2, how='left', lsuffix='_Chinese', rsuffix='_Math')
有时候用户并不关心以哪一列为键来合并,只是希望把两个表或者多个表按照纵向或者横向拼接,为这种需求,
pandas
中提供了concat
函数来实现。在
concat
中,最常用的有三个参数,它们是axis, join, keys
,分别表示拼接方向,连接形式,以及在新表中指示来自于哪一张旧表的名字。这里需要特别注意,join
和keys
与之前提到的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
可以实现多个表之间的方向拼接,如果想要把一个序列追加到表的行末或者列末,则可以分别使用append
和assign
方法。
在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=0
i=0
while(i!=len(df)):
df.iloc[i,0] = get_user_id(df.iloc[i,2])
j=j+1
i=i+1
return df
def get_user_id(v):
for i,j in name_dict.items():
if j==v:
return i
str
对象是定义在Index
或Series
上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其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]
str.split
能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数n
,是否展开为多个列expand
。
s = pd.Series(['上海市黄浦区方浜中路249号', '上海市宝山区密山路5号'])
s.str.split('[市区路]')
s.str.split('[市区路]', n=2, expand=True)
不拆成列,就返回列表 ; 拆成列就一个值一个列
关于合并一共有两个函数,分别是str.join
和str.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')
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')
str.replace
和replace
并不是一个函数,在使用字符串替换时应当使用前者。
s = pd.Series(['a_1_b','c_?'])
s.str.replace('\d|\?', 'new', regex=True)
提取既可以认为是一种返回具体元素(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的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\d+)[T|S](?P\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)]
upper, lower, title, capitalize, swapcase , strip, rstrip, lstrip
s.str.upper()
这里着重需要介绍的是pd.to_numeric
方法,它虽然不是str
对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。
其主要参数包括errors
和downcast
分别代表了非数值的处理模式和转换类型。
其中,对于不能转换为数值的有三种errors
选项,
raise, coerce, ignore
分别表示直接报错、设为缺失以及保持原来的字符串。
在数据清洗时,可以利用coerce
的设定,快速查看非数值型的行:
s[pd.to_numeric(s, errors='coerce').isna()]
count
和len
的作用分别是返回出现正则模式的次数和字符串的长度:
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','*')
在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()
通过cat
对象的categories
属性能够完成对类别的查询,那么应该如何进行“增改查删”的其他三个操作呢?
在第三章中曾提到,索引 Index
类型是无法用 index_obj[0] = item
来修改的,而 categories
被存储在 Index
中,因此 pandas
在 cat
属性上定义了若干方法来达到相同的目的。
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':'本科二年级学生'})
有序类别和无序类别可以通过as_unordered
和reorder_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进行具体的相对大小调整。
在第二章中,曾提到了字符串和数值类型序列的排序,此时就要说明分类变量的排序:只需把列的类型修改为category
后,再赋予相应的大小关系,就能正常地使用sort_index
和sort_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()
区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过
cut
和qcut
方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。
首先介绍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])
另外两个常用参数为labels
和retbins
,分别代表了区间的名字和是否返回分割点(默认不返回):
s = pd.Series([1,2])
res = pd.cut(s, bins=2, labels=['small', 'big'], retbins=True)
res[0] #分割标签
res[1] #分割点
从用法上来说,qcut
和cut
几乎没有差别,只是把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()
对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态,其中开闭状态可以指定right, left, both, neither
中的一类:
my_interval = pd.Interval(0, 1, 'right')
my_interval
其属性包含了mid, length, right, left, closed,
,分别表示中点、长度、右端点、左端点和开闭状态
使用in
可以判断元素是否属于区间
使用overlaps
可以判断两个区间是否有交集:
0.5 in my_interval
my_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
的功能类似于cut
或qcut
函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点: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')
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/length
IntervalIndex还有两个常用方法,包括contains和overlaps,分别指逐个判断每个区间是否有交集
id_demo.contains(4)
id_demo.overlaps(pd.Interval(40,60))
df.cut
在object
类型和category
类型下使用nunique
函数,并比较它们的性能。Fair, Good, Very Good, Premium, Ideal
,纯净度有八个等级,由次到好分别是I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF
,请对切割质量按照由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行由次到好的排序。cut, clarity
这两列按照由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。Very Low, Low, Mid, High, Very High
,并把按这两种分箱方法得到的category
序列依次添加到原表中。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.carat
df['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)
第一,会出现时间戳(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的简写。
第二,会出现时间差(Time deltas)的概念,即上课需要的时间,两个Timestamp
做差就得到了时间差,pandas中利用Timedelta
来表示。类似的,一系列的时间差就组成了TimedeltaIndex
, 而将它放到Series
中后,Series
的类型就变为了timedelta64[ns]
。
第三,会出现时间段(Time spans)的概念,即在8点到10点这个区间都会持续地在上课,在pandas
利用Period
来表示。类似的,一系列的时间段就组成了PeriodIndex
, 而将它放到Series
中后,Series
的类型就变为了Period
。
第四,会出现日期偏置(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
的使用频率并不高,因此将不进行讲解,而只涉及时间戳序列、时间差序列和日期偏置的相关内容。
单个时间戳的生成利用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
一组时间戳可以组成时间序列,可以用to_datetime
和date_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')#精确到小时
前面提到了datetime64[ns]
本质上可以理解为一个大整数,对于一个该类型的序列,可以使用max, min, mean
,来取得最大时间戳、最小时间戳和“平均”时间戳。
如同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'))
s
s.dt.round/ceil/floor('1H') 取整保留到小时
一般而言,时间戳序列作为索引使用。
如果想要选出某个子时间戳序列,
第一类方法是利用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()
正如在第一节中所说,时间差可以理解为两个时间戳的差,这里也可以通过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()
时间差支持的常用运算有三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算:
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 * 5
td1 + pd.Timestamp('20200101')
td1 + ts # 逐个相加
日期偏置是一种和日历相关的特殊时间差,例如回到第一节中的两个问题:如何求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
#结果显示中,没有了星期三和星期五,不计算这俩天
前面提到了关于date_range
的freq
取值可用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
所谓时序的滑窗函数,即把滑动窗口用freq
关键词代替
下面给出一个具体的应用案例:在股票市场中有一个指标为BOLL
指标,它由中轨线、上轨线、下轨线这三根线构成,具体的计算方法分别是N
日均值线、N
日均值加两倍N
日标准差线、N
日均值减两倍N
日标准差线。利用rolling
对象计算N=30
的BOLL
指标可以如下写出:
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()
重采样对象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
这七个是取对应区间的最后一个时间戳:
Datetime, Time
合并为一个时间列Datetime
,同时把它作为索引后排序。bins=50
。Series
:df['Date'] = df.Data.str.extract('([/|\w]+\s).+')[0]+df.Time
df['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
# 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()
现在我们来深入看一下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提供了两种最常用的绘图接口,在最后的思考题中请思考两种模式的主要区别
#第一种
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提供了两种最常用的绘图接口,在最后的思考题中请思考两种模式的主要区别
- 显式创建figure和axes,在上面调用绘图方法,也被称为OO模式(object-oriented style)
- 依赖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的原理或者说基础逻辑是,用Artist对象在画布(canvas)上绘制(Render)图形。
就和人作画的步骤类似:
所以matplotlib有三个层次的API:
matplotlib.backend_bases.FigureCanvas
代表了绘图区,所有的图像都是在绘图区完成的
matplotlib.backend_bases.Renderer
代表了渲染器,可以近似理解为画笔,控制如何在 FigureCanvas 上画图。
matplotlib.artist.Artist
代表了具体的图表组件,即调用了Renderer的接口在Canvas上作图。
前两者处理程序和计算机的底层交互的事项,第三项Artist就是具体的调用接口来做出我们想要的图,比如图形、文本、线条的设定。所以通常来说,我们95%的时间,都是用来和matplotlib.artist.Artist类打交道的。
Artist有两种类型:primitives
和containers
。
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
的几种类型:曲线-Line2D,矩形-Rectangle,多边形-Polygon,图像-image
在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)
其中常用的的参数有:
其他详细参数可参考Line2D官方文档
介绍两种绘制直线line常用的方法:
# 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)
其中最主要的参数是前几个:
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)');
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)
本小节重点讲述三种最常见的子类,矩形,多边形和楔型。
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)
下面是一些常用的参数:
2) bar-柱状图
matplotlib.pyplot.bar(left, height, alpha=1, width=0.8, color=, edgecolor=, label=, lw=3)
下面是一些常用的参数:
有两种方式绘制柱状图
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);
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);
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个:
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.
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个:
# 用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)
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();
容器会包含一些primitives
,并且容器还有它自身的属性。
比如Axes Artist
,它是一种容器,它包含了很多primitives
,比如Line2D
,Text
;同时,它也有自身的属性,比如xscal
,用来控制X轴是linear
还是log
的。
matplotlib.figure.Figure
是Artist
最顶层的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实例列表
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 line
、grid line
、tick label
以及axis label
的绘制,它包括坐标轴上的刻度线、刻度label
、坐标网格、坐标轴标题。通常你可以独立的配置y轴的左边刻度以及右边的刻度,也可以独立地配置x轴的上边刻度以及下边的刻度。
刻度包括主刻度和次刻度,它们都是Tick刻度对象。
Axis
也存储了用于自适应,平移以及缩放的data_interval
和view_interval
。它还有Locator实例和Formatter实例用于控制刻度线的位置以及刻度label。
每个Axis都有一个label
属性,也有主刻度列表和次刻度列表。这些ticks
是axis.XTick
和axis.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粗细
matplotlib.axis.Tick
是从Figure
到Axes
到Axis
到Tick
中最末端的容器对象。
Tick
包含了tick
、grid 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);
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)]
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);
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');
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);
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")
);
字体设置一般有全局字体设置和自定义局部字体设置两种方法。
#该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());
除了上述的简单模式,还可以使用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);
在普通的绘图中,我们可以直接通过上图的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 entries组成。一个entry包含一个key和其对应的label。
每个 legend label左面的colored/patterned marker(彩色/图案标记)
描述由key来表示的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');
matplotlib贴心地提供了许多内置的样式供用户使用,使用方法很简单,只需在python脚本的最开始输入想使用style的名称即可调用,尝试调用不同内置样式,比较区别
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
plt.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 : 16
plt.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])
在可视化中,如何选择合适的颜色和搭配组合也是需要仔细考虑的,色彩选择要能够反映出可视化图像的主旨。
从可视化编码的角度对颜色进行分析,可以将颜色分为色相、亮度和饱和度
三个视觉通道。通常来说:
色相
: 没有明显的顺序性、一般不用来表达数据量的高低,而是用来表达数据列的类别。
明度和饱和度
: 在视觉上很容易区分出优先级的高低、被用作表达顺序或者表达数据量视觉通道。
具体关于色彩理论部分的知识,不属于本教程的重点,请参阅有关拓展材料学习。
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))
In [27]:
# 当只有一个位于[0,1]的值时,表示灰度色阶
plt.plot([1,2,3],[4,5,6],color='0.5')
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')
有些图表支持使用colormap的方式配置一组颜色,从而在可视化中通过色彩的变化表达更多信息。
在matplotlib中,colormap共有五种类型:
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 plt
N = 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) sequence
p1 = 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 plt
plt.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: subplot2grid
ax1=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()
import numpy as np
import matplotlib.pyplot as plt
N = 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) sequence
p1 = 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,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 )]
In [11]:
import numpy as np
import matplotlib.pyplot as plt
men_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 bars
fig, ax = plt.subplots()
rects1 = ax.bar(ind - width/2, men_means, width, yerr=men_std, #两个柱图,宽度就除以2
color='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 of
the bar. It can be one of the following {'center', 'right', 'left'}. 参数可选:左、中、右
"""
xpos = xpos.lower() # normalize the case of the parameter
ha = {'center': 'center', 'right': 'left', 'left': 'right'}
offset = {'center': 0.5, 'right': 0.57, 'left': 0.43} # x_txt = x + w*off
for 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/H74zJw501SrVs04nU5TuXJlExsba2bMmOH2A3Pr1q3m3nvvNRUr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CS4lNTVVoaGhSklJUUhISIHWKdTpksGDB2vJkiXauHHjRQOGJDmdTjmdzsJMAwC4DD4BgXm2EyhQVDwKGcYYDR48WIsWLdL69esVExNjqy4AAFDMeRQyBg4cqA8//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 plt
np.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 = 50
fig, 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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 )]
In [124]:
# ignore warning
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.cbook as cbook
years = 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' % x
ax.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()
: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)
in
12 # volume, adj_close from the mpl-data/example directory. The record array
13 # 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 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)(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) + 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()
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
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
date = r.date.astype(‘O’)
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()
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
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
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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