pandas

Pandas

文件读写

读取

  • pd.read_csv() 读取csv
  • pd.read_excel() 读取excel
  • pd.read_txt() 读取文本文档
公共参数:
	header=None  从第几行开始读取
    index_col  指定行索引
    usecols  读取那几列 
    parse_dates  把给出的列转化为时间序列 
    nrows 读取的行数

在读取txt文件时经常遇到分隔符不是空格,read_table()有一个分隔参数叫sep自定义分隔符(可以写正则表达式)

写入

一般在写入时把 index 设置为 False ,当索引没有使用意义时就要去掉

普通写入
	to_csv  
	to_excel

以下需要安装tabulate包  
	to_markdown  
	to_latex

基本数据结构

Series

​ 是 一维 的数据结构

pd.Series(data,index,dtype,name)
参数:
	data = 值
    index = 索引
    dtype = 存储类型
    name = 序列名字
#示例
pd.Series(data = [100,'a',{'dict':5}],
          index = pd.Index(
              ['id1',20,'thrid'],
              name='my_index'),
          dtype='object', 
          name = 'my_name')
my_index
id1              100
20                 a
thrid    {'dict': 5}
Name: my_name, dtype: object

object类型

代表一种混合类型,目前pandas把 字符串数组 认为是object类型

Dataframe

​ 相当于 二维 数组,由Series组成

data =  [[1,'a',1.2],[2,'b',2.2],[3,'c',3.2]]
df = pd.DataFrame(data=data,
                index = ['row_%d'%i for i in range(3)],
                columns=['col_0','col_1','col_2'])
print(df)
       col_0 col_1  col_2
row_0      1     a    1.2
row_1      2     b    2.2
row_2      3     c    3.2

属性

  • df.values 查看值
  • df.index 查看行索引
  • df.dtypes 查看每列的类型
  • df.shape 查看形状维度
  • df.T 转置

常用基本函数

  • df.head(n) 查看数据的n行
  • df.tail(n) 查看数据的n行
  • df.info() 返回表的信息概况(如果有中文会报错)
  • df.describe(n) 汇总统计函数

统计函数

  • df.std() 标准差
  • df.var() 方差
  • df.corr() 于原始DataFrame列与列之间相关性的DataFrame对象。
  • df.quantile() 分位数

    • 所谓四分位数;即把数值由小到大排列并分成四等份,处于三个分割点位置的数值就是四分位数。
    • 第1四分位数 (Q1),又称“较小四分位数”,等于该样本中所有数值由小到大排列后第25%的数字。
    • 第2四分位数 (Q2),又称“中位数”,等于该样本中所有数值由小到大排列后第50%的数字。
    • 第3四分位数 (Q3),又称“较大四分位数”,等于该样本中所有数值由小到大排列后第75%的数字。
  • df.count() 非缺失值个数
  • df.idxmax() 最大值对应的索引

唯一值函数

  • df.unique()去掉重复值的数据输出

  • df.nunique() 数据中唯一数据的个数

  • df.value_counts() 得到重复值的数据和他出现的频数

  • df.drop_duplicates() 多列去重

    • '''
      df.drop_duplicates参数
          subset: 列名,可选,默认为None
      
          keep: {‘first’, ‘last’, False}, 默认值 ‘first’
      
          first: 保留第一次出现的重复行,删除后面的重复行。
          last: 删除重复项,除了最后一次出现。
          False: 删除所有重复项。
          inplace:布尔值,默认为False,是否直接在原数据上删除重复项或删除重复项后返回副本。(inplace=True表示直接在原来的DataFrame上删除重复项,而默认值False表示生成一个副本。)
      '''
      
    • df.duplicated()返回是否为唯一值的布尔列表,参数与drop_duplicates一致,如果元素是重复的都会标上True,否则False,drop_duplicates等价于把duplicated为True的对应行删除

替换函数

pandas中可以分为三类:

  映射替换,逻辑替换,数值替换

映射替换

  • df.replace(old, new[, max])

    • - old -- 将被替换的子字符串。
      - new -- 新字符串,用于替换old子字符串。
      - max -- 可选字符串, 替换不超过 max 次  
      

      可以直接给予 字典 来替换数据

  • 特殊方向替换

    • 指定method = ffill/bfill

    • 	用 前面最近的未被替换掉 的值进行替换
      	用 后面最近的未被替换掉 的值进行替换
      
s = pd.Series(['a',1,'b',2,1,1,'a'])
print('ffill:n',s.replace([1,2],method='ffill'))
print('nbfill:n',s.replace([1,2],method='bfill'))
ffill:
 0    a
1    a
2    b
3    b
4    b
5    b
6    a
dtype: object

bfill:
 0    a
1    b
2    b
3    a
4    a
5    a
6    a
dtype: object

逻辑替换

  • df.where(condition) 传入条件为 False 的对应进行替换
  • df.mask(condition) 传入条件为 True 的对应进行替换

传入的条件也可以是Serise对象,只要是序列一致的布尔序列即可

s_condition = pd.Series([True,False,False,True],index=s.index)
s.mask(s_condition,0)
0      0.00000
1      1.23456
2    100.00000
3      0.00000
dtype: float64

数值替换

  • df.round(n) 返回小数位为n位的四舍五入的float值

  • df.abc() 取绝对值

  • df.clip(lower, upper, axis, inplace)

    • lower : float或array_like,默认为None  
      	最小阈值。低于此阈值的所有值都将设置为它
      upper : float或array_like,默认为None
      	最大阈值。高于此阈值的所有值都将设置为它
      axis : int或string轴名称,可选
      	沿给定轴将对象与下部和上部对齐
      inplace : 布尔值,默认为False 
      	是否对数据执行操作
      

排序函数

  • sort_values() 值排序

被排序的列可以是列表来进行多列排序

print('年龄用降序排序,工资用升序排序')
print(s.head().sort_values(['年龄','工资(元)'],ascending=[False,True]))
#因为在实操种可能有一些数据相同,索引就需要多列排序的操作,排在前面的先拍
年龄用降序排序,工资用升序排序
   序号   姓名  年龄  工资(元)
1   2   黄丽  32   3600
0   1   张茜  28   2900
4   5  曾丽萍  27   2500
3   4  邓大海  26   2200
2   3  李海涛  24   2800
  • sort_index() 索引排序

可以先指定索引列(level),字符串排序由字母顺序决定

print('以名字进行索引升序排序n')
s.head().sort_index(level='姓名',ascending=True)
#看得出来是A-Z是升序,Z-A是降序
以名字进行索引升序排序

序号	姓名	年龄	工资(元)
0	1	张茜	28	2900
1	2	黄丽	32	3600
2	3	李海涛	24	2800
3	4	邓大海	26	2200
4	5	曾丽萍	27	2500

apply方法

​ apply用与DataFrame的 行迭代列迭代

  • DataFrame.apply(func,axis,raw,result_type)

    • func:
              应用于每一列或每一行的函数。
      
          axis:
              {0 或 'index',1 或 'columns'},默认 0
              沿其应用函数的轴:
                  0 或“索引”:将函数应用于每一列。
                  1 或“列”:将函数应用于每一行。
      
          raw:
              布尔值,默认为 False
              确定行或列是否作为 Series 或 ndarray 对象传递:
                  False: 将每一行或每一列作为一个系列传递给函数。
                  True:传递的函数将接收 ndarray 对象。如果您只是应用NumPy 缩减功能,这将获得更好的性能。
      
          result_type 
          {'expand', 'reduce', 'broadcast','None'},默认无
              axis=1这些仅在(列)时起作用:
                  'expand' : 类似列表的结果将变成列。
                  'reduce' :如果可能,返回一个系列,而不是扩展类似列表的结果。这与“扩展”相反。
                  'broadcast' : 结果将被广播到DataFrame的原始形状,原始索引和列将被保留。
      
              默认行为(None)取决于应用函数的返回值:类似列表的结果将作为这些结果的系列返回。但是,如果应用函数返回一个系列,这些将扩展为列。
      
df_demo = s[['年龄','工资(元)']]
#先把需要遍历的列或行提取出来
print('不用apply函数的平均值:n',df_demo.mean())

def func(x):
    res = x.mean()
    return res

print('n使用了apply函数的平均值n',df_demo.apply(func,axis=1))
不用apply函数的平均值:
 年龄         30.763889
工资(元)    3059.722222
dtype: float64

使用了apply函数的平均值
 0     1464.0
1     1816.0
2     1412.0
3     1113.0
4     1263.5
       ...  
67    1919.0
68    1617.0
69    1263.0
70    1917.5
71    2273.0
Length: 72, dtype: float64

apply的自由度很高但是代价是性能,所以一般少用apply

mad

mad 函数返回的是一个序列中偏离该序列均值的绝对值大小的均值

例如序列1,3,7,10中,均值为5.25,每一个元素偏离的绝对值为4.25,2.25,1.75,4.75,这个偏离序列的均值为3.25。

print('mad函数输出n',df_demo.mad())
print('n使用apply输出n',df_demo.apply(lambda x:(x-x.mean()).abs().mean()))
mad函数输出
 年龄         5.515432
工资(元)    600.231481
dtype: float64

使用apply输出
 年龄         5.515432
工资(元)    600.231481
dtype: float64

窗口对象

​ 滑动窗口rolling 扩张窗口expanding 指数加权窗口ewm

滑动窗口

rolling

​ 滑动窗口将window参数设置为多少,就从源数据第一个元素向左边数 window参数-1(因为包括它本身) 然后进行运算,以此向后推

num5 = pd.Series([1,2,3,4,5,6])
roller = num5.rolling(window=2)
print(roller.sum())

'''
    当窗口为2时,从第一个源数据元素向左数 window-1 个位 就是1个位
    因为0索引左边没有任何数[本位数据为1],所以为缺失值(任何数与缺失值作运算都是缺失值)
    因为1索引向左边数是1(源数据)[本位数据为2],所以求和为3
    因为2索引向左边数是2(源数据)[本位数据为3],所以为5
'''
0     NaN
1     3.0
2     5.0
3     7.0
4     9.0
5    11.0
dtype: float64

另类窗口函数

​ 他们的公共参数:periods=n

  • shift 向左取第n个元素的值
#shift:取向左第n个元素的值
num5 = pd.Series([1,3,6,10,15])
print('shift:n',num5.shift(periods=2))
print('n特殊用法(取负数)n',num5.shift(-1))
shift:
 0    NaN
1    NaN
2    1.0
3    3.0
4    6.0
dtype: float64

特殊用法(取负数)
 0     3.0
1     6.0
2    10.0
3    15.0
4     NaN
dtype: float64
  • diff 与向左第n个元素做差(与numpy不同,后者表示n阶差分)
#diff:向左取n个元素做差
num5 = pd.Series([1,3,6,10,15])
print('diff:n',num5.diff(2))
print('n特殊用法(取负数):n',num5.diff(-2))
diff:
 0    NaN
1    NaN
2    5.0
3    7.0
4    9.0
dtype: float64

特殊用法(取负数):
 0   -5.0
1   -7.0
2   -9.0
3    NaN
4    NaN
dtype: float64
  • pct_change 与向左第n个元素相比计算增长量(n可以为负,表示反方向的类似操作)
#pct_change:与向左第n个元素相比计算增长量
num5 = pd.Series([1,3,6,10,15])
print('pct_change:n',num5.pct_change(2))
print('n特殊用法(取负数):n',num5.pct_change(-2))
pct_change:
 0         NaN
1         NaN
2    5.000000
3    2.333333
4    1.500000
dtype: float64

特殊用法(取负数):
 0   -0.833333
1   -0.700000
2   -0.600000
3         NaN
4         NaN
dtype: float64

另类滑窗函数也可以用rolling函数来实现

扩展窗口

expanding

​ 累计窗口函数,与滑动窗口不同的是他的窗口会动态增大,从第一个开始向右边平移n个元素进行计算

num5 = pd.Series([1,3,6,10,15])
num5.expanding().mean()
0    1.000000
1    2.000000
2    3.333333
3    5.000000
4    7.000000
dtype: float64

索引器

基本索引

####行索引

​ 一般的索引都是中括号['列名']来索引内容,结果返回Serise(参数<=1时),如果参数>1就返回DataFrame

#读取文件
df = pd.read_csv('C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv',usecols = 
['School', 'Grade', 'Name', 'Gender','Weight', 'Transfer'])
df['Name'].head()
0      Gaopeng Yang
1    Changqiang You
2           Mei Sun
3      Xiaojuan Sun
4       Gaojuan You
Name: Name, dtype: object

 此外,若要取出单列,且列名中不包含空格,则可以用 .列名 取出,这和 [列名] 是等价的:

列索引

 如果取出单个索引对应的元素,可以使用[index],若 Series 只有单个值对应,则返回这个标量值,如果有多个值对应,则返回一个 Series

s = pd.Series([1, 2, 3, 4, 5, 6],
            index=['a', 'b', 'a', 'a', 'a', 'c'])
s
a    1
b    2
a    3
a    4
a    5
c    6
dtype: int64
索引切片

​ 如果想要取出某两个索引之间的元素,并且这两个索引是在整个索引中 唯一出现 ,就可以使用 切片 (注意:索引切片会包含两个端点 )

s['c': 'b': -2]
c    6
a    4
b    2
dtype: int64

如果前后端点的值存在 重复 ,即 非唯一值 ,那么需要经过 排序 才能使用切片

#错误案例
try:
    s['a','b']
except Exception as e:
    Err_Msg = e

Err_Msg
KeyError('key of type tuple not found and not a MultiIndex')

​ 如果使用整数切片,则会取出对应索引 位置 的值,注意这里的整数切片同 Python 中的切片一样 不包含右端点

s[1:-1:2]
b    2
a    4
dtype: int64

提示:

​ 如果不想陷入麻烦,那么请不要把纯浮点以及任何混合类型(字符串、整数、浮点类型等的混合作为索引,否则可能会在具体的操作时报错或者返回非预期的结果,并且在实际的数据分析中也不存在这样做的动机。

loc索引器

​ 是一种基于 元素 的索引器

  • 语法:df.loc[*,*]

    • 第一个 *代表行的选择,第二个 * 代表列的选择,如果省略第二个位置写作 loc[ * ] ,这个 * 是指行的筛选

    • 其中, * 的位置一共有五类合法对象

    ​ 分别是:单个元素、元素列表、元素切片、布尔列表以及函数

df = pd.read_csv('C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv',usecols = 
['School', 'Grade', 'Name', 'Gender','Weight', 'Transfer'])
df.head()
School Grade Name Gender Weight Transfer
0 Shanghai Jiao Tong University Freshman Gaopeng Yang Female 46.0 N
1 Peking University Freshman Changqiang You Male 70.0 N
2 Shanghai Jiao Tong University Senior Mei Sun Male 89.0 N
3 Fudan University Sophomore Xiaojuan Sun Female 41.0 N
4 Fudan University Sophomore Gaojuan You Male 74.0 N

####行和列

df_demo.loc['Qiang Sun', 'School'] # 返回Series,因为第一个是索引列
Name
Qiang Sun              Tsinghua University
Qiang Sun              Tsinghua University
Qiang Sun    Shanghai Jiao Tong University
Name: School, dtype: object
df_demo.loc['Quan Zhao', 'School'] # 返回单个元素
'Shanghai Jiao Tong University'

####元素列表

df_demo.loc[['Qiang Sun','Quan Zhao'], ['School','Gender']]#dataframe
School Gender
Name
Qiang Sun Tsinghua University Female
Qiang Sun Tsinghua University Female
Qiang Sun Shanghai Jiao Tong University Female
Quan Zhao Shanghai Jiao Tong University Female

####切片

​ Series 使用字符串索引时提到,如果是唯一值的起点和终点字符,那么就可以使用切片,并且包含两个端点,如果不唯一则报错

#两个端点都包含
df_demo.loc['Gaojuan You':'Gaoqiang Qian', 'School':'Gender']
School Grade Gender
Name
Gaojuan You Fudan University Sophomore Male
Xiaoli Qian Tsinghua University Freshman Female
Qiang Chu Shanghai Jiao Tong University Freshman Female
Gaoqiang Qian Tsinghua University Junior Female

​ 需要注意的是,如果 DataFrame 使用整数索引,其使用整数切片的时候和上面字符串索引的要求一致,都是 元素 切片,包含两个端点、端点不允许有重复值

df_loc_slice_demo = df_demo.copy() # 复制下来
df_loc_slice_demo.index = range(df_demo.shape[0],0,-1) #把所以设置为全部元素记录数的降序
df_loc_slice_demo.loc[5:3]# 定位器切片,两个端点都包含
School	Grade	Gender	Weight	Transfer
5	Fudan University	Junior	Female	46.0	N
4	Tsinghua University	Senior	Female	50.0	N
3	Shanghai Jiao Tong University	Senior	Female	45.0	N
df_loc_slice_demo.loc[3:5] 
# 没有返回,说明不是整数位置切片,要根据索引排序来切片
School	Grade	Gender	Weight	Transfer

####布尔索引

​  传入 loc 的布尔列表与 DataFrame 长度相同,且列表为 True 的位置所对应的行会被选中False 则会被剔除

# 选出体重超过70kg的学生
df_demo.loc[df_demo.Weight>70].head()
School Grade Gender Weight Transfer
Name
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Gaojuan You Fudan University Sophomore Male 74.0 N
Xiaopeng Zhou Shanghai Jiao Tong University Freshman Male 74.0 N
Xiaofeng Sun Tsinghua University Senior Male 71.0 N
Qiang Zheng Shanghai Jiao Tong University Senior Male 87.0 N
  • 传入元素列表,也可以通过 **isin**方法返回的布尔列表等价写出
# 选出所有大一和大四的同学信息
df_demo.loc[df_demo.Grade.isin(['Freshman', 'Senior'])].head()
#isin是dataframe的一个方法
School Grade Gender Weight Transfer
Name
Gaopeng Yang Shanghai Jiao Tong University Freshman Female 46.0 N
Changqiang You Peking University Freshman Male 70.0 N
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Xiaoli Qian Tsinghua University Freshman Female 51.0 N
Qiang Chu Shanghai Jiao Tong University Freshman Female 52.0 N
  • 对于复合条件而言,可以用 |(或), &(且), ~(取反) 的组合来实现
#选出复旦大学中体重超过70kg的大四学生,或者北大男生中体重超过80kg的非大四的学生
school1 = df_demo.School == 'Fudan University'
grade1 = df_demo.Grade == 'Senior'
weight1 = df_demo.Weight > 70
FD = school1 & grade1 & weight1

school2 = df_demo.School == 'Peking University'
grade2 = df_demo.Grade == 'Senior'
weight2 = df_demo.Weight > 80
BD = school2 & (~grade2) & weight2 # (~grade2)这里是取反

df_demo.loc[FD | BD]
School Grade Gender Weight Transfer
Name
Qiang Han Peking University Freshman Male 87.0 N
Chengpeng Zhou Fudan University Senior Male 81.0 N
Changpeng Zhao Peking University Freshman Male 83.0 N
Chengpeng Qian Fudan University Senior Male 73.0 Y

select_dtypes

  • DataFrame.select_dtypes

    参数:

    ​ include : 标量(类型)

    ​ exclude : 列表

    标量或类似列表的内容,包括/排除 的dtypes或字符串的选择。必须至少提供这些参数之一

* 要选择所有数字类型,请使用np.number或'number'

* 要选择字符串,您必须使用objectdtype,但请注意,这将返回所有object dtype 列

* 查看numpy dtype 层次结构

* 要选择日期时间,请np.datetime64使用'datetime'或 'datetime64'  
* 要选择时间增量,请np.timedelta64使用'timedelta'或 'timedelta64'  

* 要选择 Pandas 分类数据类型,请使用'category'  

* 要选择 Pandas datetimetz dtypes,请使用'datetimetz'(0.20.0 中的新功能)或'datetime64[ns, tz]'  
df_demo.select_dtypes(include=['float64','object']).head()
School Grade Gender Weight Transfer
Name
Gaopeng Yang Shanghai Jiao Tong University Freshman Female 46.0 N
Changqiang You Peking University Freshman Male 70.0 N
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Xiaojuan Sun Fudan University Sophomore Female 41.0 N
Gaojuan You Fudan University Sophomore Male 74.0 N

字符串dtypes是不允许的,使用’object’代替

函数

​  必须以前面的四种合法形式之一为返回值,并且函数的输入值为DataFrame本身(支持使用 lambda 表达式)

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# (~condition_2_2) 取反

    result = condition_1 | condition_2
    return result

df_demo.loc[condition]
School Grade Gender Weight Transfer
Name
Qiang Han Peking University Freshman Male 87.0 N
Chengpeng Zhou Fudan University Senior Male 81.0 N
Changpeng Zhao Peking University Freshman Male 83.0 N
Chengpeng Qian Fudan University Senior Male 73.0 Y
  • slice() 返回切片对象,主要作用在切片操作函数里的参数传递,由于函数无法返回(如 start: end: step )的切片形式,故返回切片时要用 slice 对象进行包装
df_demo.loc[lambda x: slice('Gaojuan You', 'Gaoqiang Qian')]#start:end:step
School Grade Gender Weight Transfer
Name
Gaojuan You Fudan University Sophomore Male 74.0 N
Xiaoli Qian Tsinghua University Freshman Female 51.0 N
Qiang Chu Shanghai Jiao Tong University Freshman Female 52.0 N
Gaoqiang Qian Tsinghua University Junior Female 50.0 N

对于 Series 也可以使用 loc 索引,其遵循的原则与 DataFrame 中用于行筛选的 loc[*] 完全一致

SettingWithCopyWarning

 在对表或者序列赋值时,应当在使用一层索引器后直接进行赋值操作,这样做是由于进行多次索引后赋值是赋在临时返回的 copy 副本上的,而没有真正修改元素从而报出 SettingWithCopyWarning 警告。

df_chain[df_chain.A!=0].B = 1
'''
   这样使用会出问题的原因就是索引后数据在内存中
   再去直接调用属性并赋值pandas就会报出警告并且'不执行'这个语句
   但是可以使用索引器来达到这个目的
'''
C:Python310libsite-packagespandascoregeneric.py:5516: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self[name] = value

​ 但是可以使用索引器来达到这个目的

df_chain.loc[df_chain.A!=0,'B'] = 1
df_chain
	A	B
0	0	0
1	1	1
2	-1	1

iloc索引器

针对位置进行筛选/查找,在相应的 * 位置处一共也有五类合法对象,分别是:整数、整数列表、整数切片、布尔列表以及函数,函数的返回值必须是前面的四类合法对象中的一个,其输入同样也为 DataFrame 本身

df_demo.iloc[1, 1] # 第二行第二列
'Freshman'
df_demo.iloc[[0, 1], [0, 1]] # 前两行前两列
School Grade
Name
Gaopeng Yang Shanghai Jiao Tong University Freshman
Changqiang You Peking University Freshman
  • 切片不包含结束端点
df_demo.iloc[1:4, 2:4] # 切片不包含结束端点
Gender Weight
Name
Changqiang You Male 70.0
Mei Sun Male 89.0
Xiaojuan Sun Female 41.0
  • 传入切片为返回值的函数
df_demo.iloc[lambda x: slice(1, 4)]
School Grade Gender Weight Transfer
Name
Changqiang You Peking University Freshman Male 70.0 N
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Xiaojuan Sun Fudan University Sophomore Female 41.0 N
  • 布尔列表

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

df_demo.iloc[(df_demo.Weight>80).values].head()
# .values 将布尔的每个值传入索引器
School Grade Gender Weight Transfer
Name
Mei Sun Shanghai Jiao Tong University Senior Male 89.0 N
Qiang Zheng Shanghai Jiao Tong University Senior Male 87.0 N
Qiang Han Peking University Freshman Male 87.0 N
Chengpeng Zhou Fudan University Senior Male 81.0 N
Feng Han Shanghai Jiao Tong University Sophomore Male 82.0 N

Series 而言同样也可以通过 iloc 返回相应位置的值或子序列

query方法

​ pandas可以支持字符串形式传入 query 方法来查询数据,表达式执行结果必须返回布尔列表

​ 优点:使用query方法可以无需重复使用DataFrame的列名来索引,但一般使用的代码长度尽量较少(在不降低可读性的前提下)

df.query('((School == "Fudan University")&'
            ' (Grade == "Senior")&'
            ' (Weight > 70))|'
            '((School == "Peking University")&'
            ' (Grade != "Senior")&'
            ' (Weight > 80))')
School Grade Name Gender Weight Transfer
38 Peking University Freshman Qiang Han Male 87.0 N
66 Fudan University Senior Chengpeng Zhou Male 81.0 N
99 Peking University Freshman Changpeng Zhao Male 83.0 N
131 Fudan University Senior Chengpeng Qian Male 73.0 Y

 在 query 表达式中,自动帮用户注册了所有来自 DataFrame列名,所有属于该 Series 的方法都可以被调用,和正常的函数调用并没有区别

对于含有空格的列名,需要使用 ‘col name’ 的(加引号)方式进行引用。

  • 在 query 中还注册了若干英语的字面用法,帮助提高可读性,例如: or, and, or, is in, not in
df.query('(Grade not in ["Freshman", "Sophomore"]) 
         			and (Gender == "Male")').head()
School Grade Name Gender Weight Transfer
2 Shanghai Jiao Tong University Senior Mei Sun Male 89.0 N
16 Tsinghua University Junior Xiaoqiang Qin Male 68.0 N
17 Tsinghua University Junior Peng Wang Male 65.0 N
18 Tsinghua University Senior Xiaofeng Sun Male 71.0 N
21 Shanghai Jiao Tong University Senior Xiaopeng Shen Male 62.0 NaN
  • 字符串中出现与列表的比较时, ==!= 分别表示元素出现在列表或没有出现在列表,等价于 is innot in
df.query('Grade == ["Junior", "Senior"]').head()
School Grade Name Gender Weight Transfer
2 Shanghai Jiao Tong University Senior Mei Sun Male 89.0 N
7 Tsinghua University Junior Gaoqiang Qian Female 50.0 N
9 Peking University Junior Juan Xu Female NaN N
11 Tsinghua University Junior Xiaoquan Lv Female 43.0 N
12 Shanghai Jiao Tong University Senior Peng You Female 48.0 NaN
  • 对于 query 中的字符串,如果要引用外部变量,只需在变量名前加 @ 符号
low, high =70, 80
df.query('Weight.between(@low, @high)').head()
School Grade Name Gender Weight Transfer
1 Peking University Freshman Changqiang You Male 70.0 N
4 Fudan University Sophomore Gaojuan You Male 74.0 N
10 Shanghai Jiao Tong University Freshman Xiaopeng Zhou Male 74.0 N
18 Tsinghua University Senior Xiaofeng Sun Male 71.0 N
35 Peking University Freshman Gaoli Zhao Male 78.0 N

随机抽样

​ 想要对样本或特征进随机抽样就可以用 sample 函数

sample(n , axis , frac , replace , weights)
	n:抽样数量  
    axis:抽样方向(0为行,1为列)
    frac:抽样比例(1100%)
    replace:是否放回(True/False)
    weights:每个样本抽样的相对概率
df_sample = pd.DataFrame({'id': list('abcde'),'value': [1, 2, 3, 4, 90]})
df_sample
id value
0 a 1
1 b 2
2 c 3
3 d 4
4 e 90

示例:

df_sample.sample(3, replace = True, weights = df_sample.value)
'''
    抽样数量为3,有放回,"weights是把vaolue整列的记录数除以整列的和"
'''
id value
4 e 90
4 e 90
4 e 90

多级索引

多级索引及其表的结构

  • 创建表便于学习
#先创建一个表,用来示例,之后会讲到
np.random.seed(0)
multi_index = pd.MultiIndex.from_product([list('ABCD'),df.Gender.unique()], names=('School', 'Gender'))# 以笛卡尔积创建行索引
multi_column = pd.MultiIndex.from_product([['Height', 'Weight'],df.Grade.unique()], names=('Indicator', 'Grade'))# 以笛卡尔积创建列索引
df_multi = pd.DataFrame(np.c_[(np.random.randn(8,4)*5 + 163).tolist(),(np.random.randn(8,4)*5 + 65).tolist()],index = multi_index,columns = multi_column).round(1)#创建表

df_multi
       	Indicator	Height								Weight
        Grade	Freshman	Senior	Sophomore	Junior	Freshman	Senior	Sophomore	Junior
School	Gender								
    A	Female	171.8		165.0		167.9	174.2	60.6		55.1		63.3		65.8
        Male	172.3		158.1		167.8	162.2	71.2		71.0		63.1		63.5
    B	Female	162.5		165.1		163.7	170.3	59.8		57.9		56.5		74.8
        Male	166.8		163.6		165.2	164.7	62.5		62.8		58.7		68.9
    C	Female	170.5		162.0		164.6	158.7	56.9		63.9		60.5		66.9
        Male	150.2		166.3		167.3	159.3	62.4		59.1		64.9		67.1
    D	Female	174.3		155.7		163.2	162.1	65.3		66.5		61.8		63.2
        Male	170.7		170.3		163.8	164.9	61.6		63.2		60.9		56.4

​ 多级索引与单层索引的表一样,具备元素值,行索引,列索引三个部分,行索引和列索引都是**MultiIndex类型,只不过索引中的元素是元组型** 而不是单层索引中的标量

  • 索引的 名字和值属性 分别可以通过 namesvalues 获得
df_multi.index.names#名字就是最外层的索引
FrozenList(['School', 'Gender'])
df_multi.columns.names#名字就是最外层的索引
FrozenList(['Indicator', 'Grade'])
df_multi.index.values#值属性就是里面内层的索引
array([('A', 'Female'), ('A', 'Male'), ('B', 'Female'), ('B', 'Male'),
       ('C', 'Female'), ('C', 'Male'), ('D', 'Female'), ('D', 'Male')],
      dtype=object)
df_multi.columns.values#值属性就是里面内层的索引
array([('Height', 'Freshman'), ('Height', 'Senior'),
       ('Height', 'Sophomore'), ('Height', 'Junior'),
       ('Weight', 'Freshman'), ('Weight', 'Senior'),
       ('Weight', 'Sophomore'), ('Weight', 'Junior')], dtype=object)
  • 如果想要得到某一层的索引,则需要通过 get_level_values(n)获得
df_multi.index.get_level_values(0) # 里面的参数是层数,以0开始
Index(['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D'], dtype='object', name='School')

多级索引中的loc索引器

# 先把索引设置成第一列和第二列
df_multi = df.set_index(['School', 'Grade'])
df_multi.head()
Name Gender Weight Transfer
School Grade
Shanghai Jiao Tong University Freshman Gaopeng Yang Female 46.0 N
Peking University Freshman Changqiang You Male 70.0 N
Shanghai Jiao Tong University Senior Mei Sun Male 89.0 N
Fudan University Sophomore Xiaojuan Sun Female 41.0 N
Sophomore Gaojuan You Male 74.0 N
  • 由于多级索引中的单个元素以元组为单位,之前介绍的 lociloc 方法可以完全照搬,只需把标量的位置替换成对应的元组
df_sorted = df_multi.sort_index()#先排序
df_sorted.loc[('Fudan University', 'Junior')].head()
						Name	Gender	Weight	Transfer
School			Grade				
Fudan University Junior	Yanli You	Female	48.0	N
				Junior	Chunqiang Chu	Male	72.0	N
				Junior	Changfeng Lv	Male	76.0	N
				Junior	Yanjuan Lv	Female	49.0	NaN
				Junior	Gaoqiang Zhou	Female	43.0	N
df_sorted.loc[[('Fudan University', 'Senior'),('Shanghai Jiao Tong University', 'Freshman')]].head()
						Name	Gender	Weight	Transfer
School			 Grade				
Fudan University Senior	Chengpeng Zheng	Female	38.0	N
                Senior	Feng Zhou	Female	47.0	N
                Senior	Gaomei Lv	Female	34.0	N
                Senior	Chunli Lv	Female	56.0	N
                Senior	Chengpeng Zhou	Male	81.0	N
df_sorted.loc[df_sorted.Weight > 70].head() # 布尔列表也是可用的
						Name	Gender	Weight	Transfer
School			  Grade				
Fudan University Freshman	Feng Wang	Male	74.0	N
                 Junior	Chunqiang Chu	Male	72.0	N
                 Junior	Changfeng Lv	Male	76.0	N
                 Senior	Chengpeng Zhou	Male	81.0	N
                 Senior	Chengpeng Qian	Male	73.0	Y
df_sorted.loc[lambda x:('Fudan University','Junior')].head()
					Name	Gender	Weight	Transfer
School			Grade				
Fudan University Junior	Yanli You	Female	48.0	N
            Junior	Chunqiang Chu	Male	72.0	N
            Junior	Changfeng Lv	Male	76.0	N
            Junior	Yanjuan Lv	Female	49.0	NaN
            Junior	Gaoqiang Zhou	Female	43.0	N
  • 当使用切片时需要注意,在单级索引中只要切片端点元素是唯一的,那么就可以进行切片,但在多级索引中,无论元组在索引中是否重复出现,都必须经过排序才能使用切片,否则报错
df_sorted.loc[('Fudan University', 'Senior'):].head()
'''
    ('Fudan University', 'Senior')这是一个整体,代表这些行后面的所有内容(:)
'''
						Name	Gender	Weight	Transfer
School			  Grade				
Fudan University Senior	Chengpeng Zheng	Female	38.0	N
                 Senior	Feng Zhou	Female	47.0	N
                 Senior	Gaomei Lv	Female	34.0	N
                 Senior	Chunli Lv	Female	56.0	N
                 Senior	Chengpeng Zhou	Male	81.0	N
df_unique = df.drop_duplicates(subset=['School','Grade']).set_index(['School', 'Grade'])
df_unique.head()
Name Gender Height Weight Transfer Test_Number Test_Date Time_Record
School Grade
Shanghai Jiao Tong University Freshman Gaopeng Yang Female 158.9 46.0 N 1 2019/10/5 0:04:34
Peking University Freshman Changqiang You Male 166.5 70.0 N 1 2019/9/4 0:04:20
Shanghai Jiao Tong University Senior Mei Sun Male 188.9 89.0 N 2 2019/9/12 0:05:22
Fudan University Sophomore Xiaojuan Sun Female NaN 41.0 N 2 2020/1/3 0:04:08
Tsinghua University Freshman Xiaoli Qian Female 158.0 51.0 N 1 2019/10/31 0:03:47
df_unique.sort_index().loc[('Fudan University', 'Senior'):].head()
'''
    sort_index()先以索引进行排序
    loc[('Fudan University', 'Senior'):] 定位('Fudan University', 'Senior')这一行后输出这一行和后面所有内容
'''
Name Gender Weight Transfer
School Grade
Fudan University Senior Chengpeng Zheng Female 38.0 N
Sophomore Xiaojuan Sun Female 41.0 N
Peking University Freshman Changqiang You Male 70.0 N
Junior Juan Xu Female NaN N
Senior Changli Lv Female 41.0 N
  • 可对多层的元素进行交叉组合后索引, 但同时需要指定 loc 的列 ,全选则用 : 表示。其中,每一层需要选中的元素用列表存放,传入 loc 的形式为 [(level_0_list, level_1_list), cols]
res = df_multi.loc[(['Peking University', 'Fudan University'],['Sophomore', 'Junior']), :]
'''
    ['Peking University', 'Fudan University']与['Sophomore', 'Junior']都是条件,但是必须指定列
    ['Peking University', 'Fudan University']是第一层索引
    ['Sophomore', 'Junior']是第二层索引
'''
res.head()
						Name	Gender	Weight	Transfer
School				Grade				
Peking University Sophomore	Changmei Xu	Female	43.0	N
                Sophomore	Xiaopeng Qin	Male	NaN	N
                Sophomore	Mei Xu	Female	39.0	N
                Sophomore	Xiaoli Zhou	Female	55.0	N
                Sophomore	Peng Han	Female	34.0	NaN

IndexSlice对象

​ 引入 IndexSlice 对象就能解决对每层进行切片,也允许将切片和布尔列表混合使用这个问题

  • Slice 对象一共有两种形式:

    ​ 第一种为 loc [ idx [ * , * ] ]

    ​ 第二种为 loc [ idx [ * , * ] , idx [ * , * ] ]

# 构造一个索引不重复的表来学习
np.random.seed(0)
L1,L2 = ['A','B','C'],['a','b','c']
mul_index1 = pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))
L3,L4 = ['D','E','F'],['d','e','f']
mul_index2 = pd.MultiIndex.from_product([L3,L4],names=('Big', 'Small'))
df_ex = pd.DataFrame(np.random.randint(-9,10,(9,9)),index=mul_index1,columns=mul_index2)

df_ex
		Big		D			E			F
		Small	d	e	f	d	e	f	d	e	f
Upper	Lower									
	A		a	3	6	-9	-6	-6	-2	0	9	-5
			b	-3	3	-8	-3	-2	5	8	-4	4
			c	-1	0	7	-4	6	6	-9	9	-6
	B		a	8	5	-2	-9	-8	0	-9	1	-6
			b	2	9	-7	-9	-9	-5	-4	-3	-1
			c	8	6	-5	0	1	-8	-8	-2	0
	C		a	-6	-3	2	5	9	-9	5	-6	3
			b	1	2	-5	-3	-5	6	-6	3	-5
			c	-1	5	6	-6	6	4	7	8	-4
  • 为了使用 silce 对象,先要进行定义
idx = pd.IndexSlice
  • loc [ idx [ * , * ] ]

    ​ 这种情况并不能进行多层分别切片,前一个 * 表示的选择,后一个 * 表示的选择,与单纯的 loc 是类似的

df_ex.loc[idx['C':, ('D', 'f'):]]
'''
    idx:
        'C'行之后的所有
        ('D', 'f')列之后的所有
    如果不加冒号,这两个都会成为条件
'''
		Big		D	E			F
		Small	f	d	e	f	d	e	f
Upper	Lower							
    C		a	2	5	9	-9	5	-6	3
       		b	-5	-3	-5	6	-6	3	-5
        	c	6	-6	6	4	7	8	-4

另外,也支持布尔序列的索引

df_ex.loc[idx[:'A', lambda x:x.sum()>0]] # 列和大于0
			Big		D		F
			Small	d	e	e
Upper		Lower			
   A		    a	3	6	9
                b	-3	3	-4
                c	-1	0	9
  • loc [ idx [ * , * ] , idx [ * , * ] ]

    ​ 这种情况能够分层进行切片,前一个 idx 指代的是索引,后一个是索引

df_ex.loc[idx[:'A', 'b':], idx['E':, 'e':]]
'''
    :'A' - 第一层行索引切到A   ,  'b': - 第二层行索引从b开始后面的所有(这里被第一层A限制了,所有才只有b,c)
    'E': - 第一层列索引从E开始切到后面所有   ,   'e': - 第二层列索引从e开始切到后面所有(因为第一层切出两个,所以分别从两个的e开始切)
'''
			Big		E		F
			Small	e	f	e	f
Upper		Lower				
	A			b	-2	5	-4	4
				c	6	6	9	-6

此方法不支持使用函数

多级索引的构造

​ 常用的有 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'])
MultiIndex([('a', 'cat'),
            ('a', 'dog'),
            ('b', 'cat'),
            ('b', 'dog')],
           names=['First', 'Second'])
  • from_arrays根据传入列表对相应层进行构造
my_array = [list('aabb'), ['cat', 'dog']*2]
pd.MultiIndex.from_arrays(my_array, names=['First','Second'])
MultiIndex([('a', 'cat'),
            ('a', 'dog'),
            ('b', 'cat'),
            ('b', 'dog')],
           names=['First', 'Second'])
  • from_product根据给定多个列表的笛卡尔积进行构造

    笛卡尔积: 就是两个列表的每个元素与另一个列表的每个元素相乘(这里是组合)

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

##索引的常用方法

的交换和删除

#创建一个3层索引便于学习
np.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
                Big		C				D
                Small	c		d		c		d
                Other	cat	dog	cat	dog	cat	dog	cat	dog
Upper	Lower	Extra								
    A		a	alpha	3	6	-9	-6	-6	-2	0	9
           		beta	-5	-3	3	-8	-3	-2	5	8
        	b	alpha	-4	4	-1	0	7	-4	6	6
            	beta	-9	9	-6	8	5	-2	-9	-8
    B		a	alpha	0	-9	1	-6	2	9	-7	-9
            	beta	-9	-5	-4	-3	-1	8	6	-5
        	b	alpha	0	1	-8	-8	-2	0	-6	-3
            	beta	2	5	9	-9	5	-6	3	1

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

  • swaplevel 交换两个层
'''
    DataFrame.swaplevel(i=- 2, j=- 1, axis=0)
    相会交换索引中的i层和j层,默认是交换索引的两个最内层
    参数:        
        i, j: int 或 str
            交换的索引的两个层级。可以将级别名称作为字符串传递。
            
        axis:{0 或 'index',1 或 'columns'},默认 0
            交换级别的轴。0 或 'index' 表示按行,1 或 'columns' 表示按列。
'''
df_ex.swaplevel(0,2,axis=1).head() # 列索引的第一层和第三层交换
                Other	cat	dog	cat	dog	cat	dog	cat	dog
                Small	c	c	d	d	c	c	d	d
                Big		C	C	C	C	D	D	D	D
Upper	Lower	Extra								
    A		a	alpha	3	6	-9	-6	-6	-2	0	 9
            	beta	-5	-3	3	-8	-3	-2	5  	 8
        	b	alpha	-4	4	-1	0	7	-4	6	 6
            	beta	-9	9	-6	8	5	-2	-9	-8
    B		a	alpha	0	-9	1	-6	2	9	-7	-9
  • reorder_levels交换任意层级
'''
	DataFrame.reorder_levels(order, axis=0)
	使用输入顺序重新排列索引层级。 不能降低或复制层级
	参数
        order: int列表 或 str的列表
        	代表新级别顺序的列表。通过数字(位置)或按键(标签)来参考水平。

        axis:{0 或 'index',1 或 'columns'},默认 0
        	在哪里重新排序级别。
'''
df_ex.reorder_levels([2,0,1],axis=0).head() # 列表数字指代原来索引中的层
                Big		C				D
                Small	c		d		c		d
                Other	cat	dog	cat	dog	cat	dog	cat	dog
Extra	Upper	Lower								
    alpha	A		a	3	6	-9	-6	-6	-2	0	9
    beta	A		a	-5	-3	3	-8	-3	-2	5	8
    alpha	A		b	-4	4	-1	0	7	-4	6	6
    beta	A		b	-9	9	-6	8	5	-2	-9	-8
    alpha	B		a	0	-9	1	-6	2	9	-7	-9

这里只涉及 行或列索引 内部的交换

  • 若想要删除某一层的索引,可以使用 droplevel 方法
df_ex.droplevel(1,axis=1)
                Big		C				D
                Other	cat	dog	cat	dog	cat	dog	cat	dog
Upper	Lower	Extra								
    A		a	alpha	3	6	-9	-6	-6	-2	0	9
            	beta	-5	-3	3	-8	-3	-2	5	8
        	b	alpha	-4	4	-1	0	7	-4	6	6
            	beta	-9	9	-6	8	5	-2	-9	-8
    B		a	alpha	0	-9	1	-6	2	9	-7	-9
            	beta	-9	-5	-4	-3	-1	8	6	-5
        	b	alpha	0	1	-8	-8	-2	0	-6	-3
            	beta	2	5	9	-9	5	-6	3	1

###属性的修改

  • rename_axis 可以对索引层的名字进行修改,常用的修改方式是传入字典的映射
df_ex.rename_axis(index={'Upper':'Changed_row'},columns={'Other':'Changed_Col'}).head()
                Big			C				D
                Small		c		d		c		d
                Changed_Col	cat	dog	cat	dog	cat	dog	cat	dog
Changed_row	Lower	Extra								
        A		a	alpha	3	6	-9	-6	-6	-2	0	9
            	    beta	-5	-3	3	-8	-3	-2	5	8
            	b	alpha	-4	4	-1	0	7	-4	6	6
            	    beta	-9	9	-6	8	5	-2	-9	-8
        B		a	alpha	0	-9	1	-6	2	9	-7	-9
  • rename可以对索引的值进行修改,如果是多级索引需要指定层号(level)
df_ex.rename(columns={'cat':'not_cat'},level=2).head()
               	Big		C						D
                Small	c			d			c			d
                Other	not_cat	dog	not_cat	dog	not_cat	dog	not_cat	dog
Upper	Lower	Extra								
    A		a	alpha	3		6		-9	-6		-6	-2		0	9
            	beta	-5		-3		3	-8		-3	-2		5	8
        	b	alpha	-4		4		-1	0		7	-4		6	6
            	beta	-9		9		-6	8		5	-2		-9	-8
    B		a	alpha	0		-9		1	-6		2	9		-7	-9

​ 也可以给**rename传入函数**,其输入的值就是索引

df_ex.rename(index=lambda x:str.upper(x),level=2).head()
                Big		C				D
                Small	c		d		c		d
                Other	cat	dog	cat	dog	cat	dog	cat	dog
Upper	Lower	Extra								
    A		a	ALPHA	3	6	-9	-6	-6	-2	0	9
            	BETA	-5	-3	3	-8	-3	-2	5	8
        	b	ALPHA	-4	4	-1	0	7	-4	6	6
            	BETA	-9	9	-6	8	5	-2	-9	-8
    B		a	ALPHA	0	-9	1	-6	2	9	-7	-9

​ 对于整个索引的元素替换,可以利用迭代器实现

'''
    iter():生成迭代器
    next() 返回迭代器的下一个项目
    next() 函数要和生成迭代器的 iter() 函数一起使用
'''
new_values = iter(list('abcdefgh'))
df_ex.rename(index=lambda x:next(new_values),level=2)
                Big		C				D
                Small	c		d		c		d
                Other	cat	dog	cat	dog	cat	dog	cat	dog
Upper	Lower	Extra								
    A		a		a	3	6	-9	-6	-6	-2	0	9
            		b	-5	-3	3	-8	-3	-2	5	8
        	b		c	-4	4	-1	0	7	-4	6	6
            		d	-9	9	-6	8	5	-2	-9	-8
    B		a		e	0	-9	1	-6	2	9	-7	-9
            		f	-9	-5	-4	-3	-1	8	6	-5
        	b		g	0	1	-8	-8	-2	0	-6	-3
            		h	2	5	9	-9	5	-6	3	1
  • map达到更改索引值

    ​ 它是定义在 Index 上的方法

    ​ 它传入的不是层的标量值,而是直接传入索引的元组

df_temp = df_ex.copy()
new_idx = df_temp.index.map(lambda x: (x[0],x[1],str.upper(x[2])))
print(new_idx)
#Python upper() 方法将字符串中的小写字母转为大写字母。
#把index放进map里面运行

df_temp.index = new_idx
df_temp.head()
MultiIndex([('A', 'a', 'ALPHA'),
            ('A', 'a',  'BETA'),
            ('A', 'b', 'ALPHA'),
            ('A', 'b',  'BETA'),
            ('B', 'a', 'ALPHA'),
            ('B', 'a',  'BETA'),
            ('B', 'b', 'ALPHA'),
            ('B', 'b',  'BETA')],
           names=['Upper', 'Lower', 'Extra'])

                Big		C				D
                Small	c		d		c		d
                Other	cat	dog	cat	dog	cat	dog	cat	dog
Upper	Lower	Extra								
    A		a	ALPHA	3	6	-9	-6	-6	-2	0	9
         	   	BETA	-5	-3	3	-8	-3	-2	5	8
        	b	ALPHA	-4	4	-1	0	7	-4	6	6
            	BETA	-9	9	-6	8	5	-2	-9	-8
    B		a	ALPHA	0	-9	1	-6	2	9	-7	-9

设置与重置

#构建新表便于学习
df_new = pd.DataFrame({'A':list('aacd'),'B':list('PQRT'),'C':[1,2,3,4]})
df_new
A B C
0 a P 1
1 a Q 2
2 c R 3
3 d T 4
  • set_index设置索引

    ​ 主要参数是append,表示是否保留原来的索引,直接把设定的索引添加到原来的索引内层

df_new.set_index('A')#直接把A列设置为索引层
B C
A
a P 1
a Q 2
c R 3
d T 4
df_new.set_index('A', append=True)#列索引A被添加到内层
B C
A
0 a P 1
1 a Q 2
2 c R 3
3 d T 4

如果 想要添加索引的列 没有出现在其中,那么可以直接在参数中传入相应的 Series

my_index = pd.Series(list('WXYZ'), name='D')
df_new = df_new.set_index(['A', my_index])
#my_index 插入了列名为D的列
df_new
B C
A D
a W P 1
X Q 2
c Y R 3
d Z T 4
  • reset_index重设索引

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

df_new.reset_index(['D'])#没有丢弃去掉的索引,而是从第二层返回到了第一层
D B C
A
a W P 1
a X Q 2
c Y R 3
d Z T 4
df_new.reset_index(['D'], drop=True)#直接丢弃去掉的索引
B C
A
a P 1
a Q 2
c R 3
d T 4

如果 reset_index()不加参数 ,就是重置dataframe的索引,由默认索引(0…,A…)代替

###变形

  • reindex更改行索引

  • reindex_like仿照传入的表索引作为自己的表索引

    没有给出值的用**np.nan**填充

  • reindex

df_reindex = pd.DataFrame({"Weight":[60,70,80],"Height":[176,180,179]},index=['1001','1003','1002'])
print('原表')
df_reindex

原表

Weight Height
1001 60 176
1003 70 180
1002 80 179
print('修改后')
df_reindex.reindex(index=['1001','1002','1003','1004'],columns=['Weight','Gender'])

修改后

Weight Gender
1001 60.0 NaN
1002 80.0 NaN
1003 70.0 NaN
1004 NaN NaN
  • reindex_like
df_existed = pd.DataFrame(index=['1001','1002','1003','1004'],columns=['Weight','Gender'])
print('原表')
df_existed

原表

Weight Gender
1001 NaN NaN
1002 NaN NaN
1003 NaN NaN
1004 NaN NaN
print('修改后')
df_reindex.reindex_like(df_existed)

修改后

Weight Gender
1001 60.0 NaN
1002 80.0 NaN
1003 70.0 NaN
1004 NaN NaN

###运算

​ 经常会有一种利用集合运算来取出符合条件行的需求,对索引可以使用集合的运算

​ 由于集合的元素是互异的,但是索引中可能有相同的元素,先用 unique 去重后再进行运算。

# 创建表
df_set_1 = pd.DataFrame([[0,1],[1,2],[3,4]],index = pd.Index(['a','b','a'],name='id1'))
df_set_2 = pd.DataFrame([[4,5],[2,6],[7,1]],index = pd.Index(['b','b','c'],name='id2'))

# 去重
id1, id2 = df_set_1.index.unique(), df_set_2.index.unique()

print(id1)
print(id2)
Index(['a', 'b'], dtype='object', name='id1')
Index(['b', 'c'], dtype='object', name='id2')
id1.intersection(id2)#交集
Index(['b'], dtype='object')
id1.union(id2)#并集
Index(['a', 'b', 'c'], dtype='object')
id1.difference(id2)#返回一个新的Index,其中Index不在给出元组里面
Index(['a'], dtype='object')
id1.symmetric_difference(id2)#返回两个index中不一样的元素
Index(['a', 'c'], dtype='object')

分组

​ 明确三个要素:分组依据数据来源操作及其返回结果

df.groupby(分组依据)[数据来源].使用操作
#读取表演示
df = pd.read_csv('C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv')
df.head()
School Grade Name Gender Height Weight Transfer Test_Number Test_Date Time_Record
0 Shanghai Jiao Tong University Freshman Gaopeng Yang Female 158.9 46.0 N 1 2019/10/5 0:04:34
1 Peking University Freshman Changqiang You Male 166.5 70.0 N 1 2019/9/4 0:04:20
2 Shanghai Jiao Tong University Senior Mei Sun Male 188.9 89.0 N 2 2019/9/12 0:05:22
3 Fudan University Sophomore Xiaojuan Sun Female NaN 41.0 N 2 2020/1/3 0:04:08
4 Fudan University Sophomore Gaojuan You Male 174.0 74.0 N
#按照性别统计身高中位数
df.groupby('Gender')['Height'].median()
Gender
Female    159.6
Male      173.4
Name: Height, dtype: float64
  • 需要根据多个维度进行分组,只需在 groupby 中传入相应列名构成的列表即可
# 根据学校和性别进行分组
df.groupby(['School', 'Gender'])['Height'].mean()
School                         Gender
Fudan University               Female    158.776923
                               Male      174.212500
Peking University              Female    158.666667
                               Male      172.030000
Shanghai Jiao Tong University  Female    159.122500
                               Male      176.760000
Tsinghua University            Female    159.753333
                               Male      171.638889
Name: Height, dtype: float64
  • 也可以通过条件进行分组
condition = df.Weight > df.Weight.mean()
df.groupby(condition)['Height'].mean()
Weight
False    159.034646
True     172.705357
Name: Height, dtype: float64
  • 由此可以看出是以分组依据的unique值来分组,传入的序列都会被去重
# 通过 drop_duplicates 就能知道具体的组类别
df[['School', 'Gender']].drop_duplicates()
School Gender
0 Shanghai Jiao Tong University Female
1 Peking University Male
2 Shanghai Jiao Tong University Male
3 Fudan University Female
4 Fudan University Male
5 Tsinghua University Female
9 Peking University Female
16 Tsinghua University Male
df.groupby([df['School'], df['Gender']])['Height'].mean()
#以上方去去重字段分组
School                         Gender
Fudan University               Female    158.776923
                               Male      174.212500
Peking University              Female    158.666667
                               Male      172.030000
Shanghai Jiao Tong University  Female    159.122500
                               Male      176.760000
Tsinghua University            Female    159.753333
                               Male      171.638889
Name: Height, dtype: float64

###Groupby对象

​ 可以先将分组依据定义成对象

gb = df.groupby(['School', 'Grade'])
gb
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001C2A65BD420>
  • ngroups属性获取分组个数
gb.ngroups
16
  • groups获取 组名映射到主索引列表 的字典
res = gb.groups
res.keys()# 字典的值由于是索引,元素个数过多,此处只展示字典的键
dict_keys([('Fudan University', 'Freshman'), ('Fudan University', 'Junior'), ('Fudan University', 'Senior'), ('Fudan University', 'Sophomore'), ('Peking University', 'Freshman'), ('Peking University', 'Junior'), ('Peking University', 'Senior'), ('Peking University', 'Sophomore'), ('Shanghai Jiao Tong University', 'Freshman'), ('Shanghai Jiao Tong University', 'Junior'), ('Shanghai Jiao Tong University', 'Senior'), ('Shanghai Jiao Tong University', 'Sophomore'), ('Tsinghua University', 'Freshman'), ('Tsinghua University', 'Junior'), ('Tsinghua University', 'Senior'), ('Tsinghua University', 'Sophomore')])
  • size统计每个组的元素个数(在DataFrame中是表长乘表宽)
gb.size()
School                         Grade    
Fudan University               Freshman      9
                               Junior       12
                               Senior       11
                               Sophomore     8
Peking University              Freshman     13
                               Junior        8
                               Senior        8
                               Sophomore     5
Shanghai Jiao Tong University  Freshman     13
                               Junior       17
                               Senior       22
                               Sophomore     5
Tsinghua University            Freshman     17
                               Junior       22
                               Senior       14
                               Sophomore    16
dtype: int64
  • get_group直接获取所在组对应的行,必须给出组的具体名字
gb.get_group(('Peking University', 'Freshman'))
School Grade Name Gender Height Weight Transfer Test_Number Test_Date Time_Record
1 Peking University Freshman Changqiang You Male 166.5 70.0 N 1 2019/9/4 0:04:20
32 Peking University Freshman Gaopeng Shi Female 162.9 48.0 N 1 2019/9/12 0:04:58
35 Peking University Freshman Gaoli Zhao Male 175.4 78.0 N 2 2019/10/8 0:03:32
36 Peking University Freshman Xiaojuan Qin Male NaN 79.0 Y 1 2019/12/10 0:04:10
38 Peking University Freshman Qiang Han Male 185.3 87.0 N 3 2020/1/7 0:03:58
45 Peking University Freshman Quan Chu Female 154.7 43.0 N 1 2019/11/28 0:04:47
54 Peking University Freshman Xiaojuan Chu Male 162.4 58.0 Y 3 2019/11/29 0:03:42
57 Peking University Freshman Changquan Chu Female 159.6 45.0 N 2 2019/12/9 0:04:18
88 Peking University Freshman Xiaopeng Han Female 164.1 53.0 N 1 2019/12/18 0:05:20
96 Peking University Freshman Changmei Feng Female 163.8 56.0 N 3 2019/11/8 0:04:41
99 Peking University Freshman Changpeng Zhao Male 181.3 83.0 N 2 2019/10/24 0:04:08
140 Peking University Freshman Qiang Zhang Female 152.7 43.0 N 1 2019/11/30 0:05:27
185 Peking University Freshman Chunmei Wang Female 151.2 43.0 N 2 2019/12/10 0:04:24

聚合函数

内置聚合函数

max/min/mean/median/count/all/any/idxmax/idxmin
mad/nunique/skew/quantile/sum/std/var/sem/size/prod 
gb = df.groupby('Gender')['Height']
  • quantile给定数据的四分位数
'''
    所谓四分位数;即把数值由小到大排列并分成四等份,处于三个分割点位置的数值就是四分位数。

    第1四分位数 (Q1),又称“较小四分位数”,等于该样本中所有数值由小到大排列后第25%的数字。
    第2四分位数 (Q2),又称“中位数”,等于该样本中所有数值由小到大排列后第50%的数字。
    第3四分位数 (Q3),又称“较大四分位数”,等于该样本中所有数值由小到大排列后第75%的数字。
'''
gb.quantile(0.95)#给定数据(数组元素)的分位数
  • 这些聚合函数当传入的数据来源包含多个列时,将按照进行迭代计算
gb = df.groupby('Gender')[['Height', 'Weight']]
gb.max()
		Height	Weight
Gender		
Female	170.2	63.0
Male	193.9	89.0

groupby对象必须聚合后才会输出DataFrame

agg方法

​ 传入函数并运行的是数据源

  • 使用多个函数

    ​ 当使用多个聚合函数时,需要用列表的形式把内置聚合函数对应的字符串传入,先前提到的所有字符串都是合法的

gb.agg(['sum', 'idxmax', 'skew'])
       Height					Weight
        sum		idxmax	skew	 	sum	  idxmax	skew
Gender						
Female	21014.0	 28 	-0.219253	6469.0	28	-0.268482
Male	8854.9	 193	0.437535	3929.0	2	-0.332393

从结果看,此时列索引为多级索引,第一层为数据源,第二层为使用的聚合方法,分别逐一队列使用聚合,因此结果为6列

  • 对特定的列使用特定的聚合函数

    ​ 通过构造字典传入 agg 中实现

    列名为键,聚合字符串或字符串列表为值

gb.agg({'Height':['mean','max'], 'Weight':'count'})#列名为键,聚合字符串为值,但是这里只是数据来源,不是分组依据,还是对性别进行分组
        Height				Weight
         mean		max		count
Gender			
Female	159.19697	170.2	135
Male	173.62549	193.9	54		
  • 自定义函数

    ​ 必须注意传入函数的参数是之前数据源中的列,逐列进行计算

gb.agg(lambda x: x.mean()-x.min())
	Height	Weight
Gender		
Female	13.79697	13.918519
Male	17.92549	21.759259

​ 由于传入的是,因此列上的方法和属性都是可以在函数中使用的,只需保证返回值是标量即可

def my_func(s):#传入的是列
    res = 'High'
    if s.mean() <= df[s.name].mean():
        res = 'Low'
    return res

gb.agg(my_func)
		Height	Weight
Gender		
Female	Low		Low
Male	High	High
  • 聚合结果的重命名

  只需将上述函数的位置改写成元组,元组第一个元素为新名字第二个位置为原来的函数,也可以使用聚合字符串和自定义函数

gb.agg([('range', lambda x: x.max()-x.min()),
        	('my_sum', 'sum')])
		Height			Weight
		range	my_sum	range	my_sum
Gender				
Female	24.8	21014.0	29.0	6469.0
Male	38.2	8854.9	38.0	3929.0
gb.agg({'Height': [('my_func', my_func), 'sum'],
        'Weight': lambda x:x.max()})
#Height对应两个聚合的元素
		Height				Weight
		my_func		sum		<lambda>
Gender			
Female	Low			21014.0		63.0
Male	High		8854.9		89.0
  • 使用对一个或者多个列使用单个聚合的时,重命名需要加方括号
gb.agg([('my_sum', 'sum')])#必须加中括号
		Height	Weight
		my_sum	my_sum
Gender		
Female	21014.0	6469.0
Male	8854.9	3929.0
gb.agg({'Height': [('my_func', my_func), 'sum'],
        'Weight': [('range', lambda x:x.max())]})
		Height				Weight
		my_func		sum		range
Gender			
Female	Low		21014.0		63.0
Male	High	8854.9		89.0	

变换和过滤

#初始化
df = pd.read_csv(
    'C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv')
gb = df.groupby('Gender')[['Height', 'Weight']]

transform方法

​ 传入的是数据源(与agg相同),按照进行计算,且不能通过传入字典来对指定列使用特定的变换,运行完成后将其给DataFrame填充

最常用的内置变换函数是累计函数:

cumcount / cumsum / cumprod / cummax / cummin

transform方法与这些是一样的

gb.cummax().head()#返回一个DataFrame或Series轴上的累积最大值
	Height	Weight
0	158.9	46.0
1	166.5	70.0
2	188.9	89.0
3	NaN		46.0
4	188.9	89.0

参数:

'''
    DataFrameGroupBy.transform(func,*args,engine = None,engine_kwargs = None,** kwargs)
        func:函数
        
        *args:传入函数从参数
        
        engine:引擎 默认None
            'cython':通过 cython 的 C 扩展运行函数。
            'numba': 通过从 numba.JIT 编译的代码运行函数。
            None:默认为'cython'或全局设置compute.use_numba

        engine_kwargs字典,默认None
            对于'cython'引擎,不接受engine_kwargs
                如果engine默认,那就不能接收字典
            对于'numba'引擎,引擎可以接受nopython,nogil 和parallel字典键。
                这些值必须是True或 False。引擎 的默认值engine_kwargs是并将应用
                于函数'numba'{'nopython': True, 'nogil': False, 'parallel': 
                False}
'''

示例:

gb.transform(lambda x: (x-x.mean())/x.std()).head()
		Height	Weight
0	-0.058760	-0.354888
1	-1.010925	-0.355000
2	2.167063	2.089498
3		NaN		-1.279789
4	0.053133	0.159631
  • transform只能返回同长度的序列,实际上也可以返回标量(函数运行后返回单个值),这会使得结果会被广播道整个组
gb.transform('mean').head() # 传入返回标量的函数也是可以的
#因为mean是平均值函数返回的是单值,在transform的作用下就会广播成每组求平均值后返回

transform与agg的区别

  • transform的数据是填充到分组对象的每列上,agg只是生成了一个最终的聚合结果
df = pd.read_csv(
    'C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv')
gb = df.groupby('Gender')[['Height', 'Weight']]

transform:

transform = gb.transform('mean')
        Height     Weight
0    159.19697  47.918519
1    173.62549  72.759259
2    173.62549  72.759259
3    159.19697  47.918519
4    173.62549  72.759259

'''
	数据来源是['Height', 'Weight'],但是结果没有给出分组依据,而是把聚合结果直接填充到Gender对应的位置上(Female,Male对应的行填充了对应的值)
'''

agg:

agg = gb.agg('mean')
           Height     Weight
Gender                      
Female  159.19697  47.918519
Male    173.62549  72.759259

'''
	直接返回分组结果,不会把值进行每行填充,所以它也不具备直接添加进源DataFrame的功能
'''

agg不具备直接添加进源表的功能,transform可以直接添加

组索引与过滤

​ groupby对象中定义了**filter函数进行组的筛选**,传入的参数也是数据源,参数中的函数只允许返回布尔值

参数:

'''
    DataFrameGroupBy.filter(func, dropna=True, *args, **kwargs)
    返回DataFrame的副本,不包括过滤的元素。如果组中的元素为False,则会过滤。 
    参数:
        func:函数,返回True/False
        dropna:丢弃为False的组。默认为True
    返回:DataFrame
'''
gb.filter(lambda x: x.shape[0] > 100).head()

#####apply

 在设计上, apply 的自定义函数传入参数与 filter 完全一致只不过后者只允许返回布尔值

# 人体的BMI指标
def BMI(x):
    Height = x['Height']/100
    Weight = x['Weight']
    BMI_value = Weight/Height**2
    return BMI_value.mean()

gb.apply(BMI)

apply函数根据返回值情况来输出不同的结果

  • 1:返回标量

    ​ 得到结果的是 Series ,索引与 agg 的结果一致

gb = df.groupby(['Gender','Test_Number'])[['Height','Weight']]
gb.apply(lambda x: 0)
Gender  Test_Number
Female  1              0
        2              0
        3              0
Male    1              0
        2              0
        3              0
dtype: int64
gb.apply(lambda x: [0, 0]) # 虽然是列表,但是作为返回值仍然看作标量
Gender  Test_Number
Female  1              [0, 0]
        2              [0, 0]
        3              [0, 0]
Male    1              [0, 0]
        2              [0, 0]
        3              [0, 0]
dtype: object
  • 返回Series

    ​ 得到结果是 DataFrame ,行索引与标量情况一致,列索引为 Series 的索引

gb.apply(lambda x: pd.Series([0,0],index=['a','b']))
					a	b
Gender	Test_Number		
Female			1	0	0
				2	0	0
				3	0	0
Male			1	0	0
				2	0	0
				3	0	0
  • 返回DataFrame

    ​ 得到结果是 DataFrame ,行索引最内层在每个组原先 agg 的结果索引上,再加一层返回的 DataFrame 行索引,同时分组结果 DataFrame 的列索引和返回的 DataFrame 列索引一致

gb.apply(lambda x: pd.DataFrame(np.ones((2,2)),index = ['a','b'],columns=pd.Index([('w','x'),('y','z')])))
						w	y
						x	z
Gender	Test_Number			
Female			1	a	1.0	1.0
					b	1.0	1.0
				2	a	1.0	1.0
					b	1.0	1.0
				3	a	1.0	1.0
					b	1.0	1.0
Male			1	a	1.0	1.0
					b	1.0	1.0
				2	a	1.0	1.0
					b	1.0	1.0
				3	a	1.0	1.0
					b	1.0	1.0

最后需要强调的是, apply 函数的灵活性是以牺牲一定性能为代价换得的,除非需要使用跨列处理的分组处理,否则应当使用其他专门设计的 groupby 对象方法,否则在性能上会存在较大的差距。同时,在使用聚合函数和变换函数时,也应当优先使用内置函数,它们经过了高度的性能优化,一般而言在速度上都会快于用自定义函数来实现。

变形

长宽表的变形

所有的变形都是Excel的数据透视表

概念:

​ 一个表把数据存入列中,他就是关于列的长表

​ 一个表把数据作为列名,列中的元素是与这个列相关的特征数值,他就是关于列名的宽表

a = pd.DataFrame({'Gender':['F','F','M','M'],'Height':[163, 160, 175, 180]})#长表
print(a)
pd.DataFrame({'Height: F':[163, 160],'Height: M':[175, 180]})#宽表
 	 Gender  Height
0      F     163
1      F     160
2      M     175
3      M     180

	Height: F	Height: M
0	163			175
1	160			180

显然这两张表从信息上是完全等价的,它们包含相同的身高统计数值,只是这些数值的呈现方式不同,而其呈现方式主要又与性别一列选择的布局模式有关,即到底是以 long 的状态存储还是以 wide 的状态存储。因此, pandas 针对此类长宽表的变形操作设计了一些有关的变形函数。

变形函数

#####长表变宽表

  • pivot 长表变宽表

参数:

'''
    DataFrame.pivot(index=None, columns=None, values=None)
    根据列值重塑数据(生成一个“透视”表)。 使用来自指定索引/列的唯一值来形成结果DataFrame的轴,此函数不支持数据聚合
    参数:
        index:str 或 object 或 list(str) 可选
            用作于新表的行索引。如果没有,则使用现有索引
        
        columns:str 或 object 或 list(str)
            用作于新表的列索引

        values:str 或 object 或 前面的列表 可选
            用作于新表的值 如果没有指定,将使用所有剩余的列,并且结果将具有分层索引的列

        return:返回重构的 DataFrame
'''

创建表便于学习

df = pd.DataFrame({'Class':[1,1,2,2],'Name':['San Zhang','San Zhang','Si Li','Si Li'],'Subject':['Chinese','Math','Chinese','Math'],'Grade':[80,75,90,85]})
df
	Class	Name	Subject	Grade
0	1	San Zhang	Chinese	80
1	1	San Zhang	Math	75
2	2	Si Li		Chinese	90
3	2	Si Li		Math	85

变形最重要的三个要素:

变形后的行索引、需要转到列索引的列,以及这些列和行索引对应的数值

他们分别对应了pivot方法中

index,columns,values参数

列索引column对应的列的 唯一值(unique)

行索引index对应的列的 唯一值(unique)

​ 而values对应了想要展示的数值列

df.pivot(index='Name', columns='Subject', values='Grade')
Subject		Chinese		Math
	Name		
San Zhang		80		75
	Si Li		90		85

由于pivot进行变形操作要满足唯一性要求,行列对应的值必须唯一,所以行列的组合必须是唯一的

df.loc[1, 'Subject'] = 'Chinese'#如果把列索引的Subject改成与值相同的Chinese就会报错
try:
    df.pivot(index='Name',columns='Subject',values='Grade')
except Exception as e:
    Err_Msg = e

Err_Msg
ValueError('Index contains duplicate entries, cannot reshape')

pivot 相关的三个参数允许传入列表,这也意味着会返回多级索引

创建表便于学习

df = pd.DataFrame({'Class':[1, 1, 2, 2, 1, 1, 2, 2],'Name':['San Zhang', 'San Zhang', 'Si Li', 'Si Li','San Zhang', 'San Zhang', 'Si Li', 'Si Li'],'Examination': ['Mid', 'Final', 'Mid', 'Final', 'Mid', 'Final', 'Mid', 'Final'],'Subject':['Chinese', 'Chinese', 'Chinese', 'Chinese', 'Math', 'Math', 'Math', 'Math'],'Grade':[80, 75, 85, 65, 90, 85, 92, 88],'rank':[10, 15, 21, 15, 20, 7, 6, 2]})
df
	Class	Name	Examination	Subject	Grade	rank
0		1	San Zhang	Mid		Chinese		80	10
1		1	San Zhang	Final	Chinese		75	15
2		2	Si Li		Mid		Chinese		85	21
3		2	Si Li		Final	Chinese		65	15
4		1	San Zhang	Mid		Math		90	20
5		1	San Zhang	Final	Math		85	7
6		2	Si Li		Mid		Math		92	6
7		2	Si Li		Final	Math		88	2

示例:

pivot_multi = df.pivot(index = ['Class', 'Name'],
                        columns = ['Subject','Examination'],
                        values = ['Grade','rank'])
'''
    这其实就是数据透视表,把行和列放进去,对应的就是值,但是必须是唯一值
'''
pivot_multi
				Grade					rank
	Subject		Chinese		Math		Chinese		Math
	Examination	Mid	Final	Mid	Final	Mid	Final	Mid	Final
Class	Name								
1	San Zhang	80	75		90	85		10	15		20	7
2	Si Li		85	65		92	88		21	15		6	2

​ 根据唯一性原则,新表的行索引等价于对index中的多列使用drop_duplicates去重,而**列索引长度为values中的元素乘以columns的唯一组合数量(**与index类型)

  • pivot_table 不依赖唯一性条件的 长表变宽表

    pivot 的使用依赖于唯一性条件,那如果不满足唯一性条件,那么必须通过聚合操作使得相同行列组合对应的多个值变为一个值。

创建表方便学习

df = pd.DataFrame({'Name':['San Zhang', 'San Zhang','San Zhang', 'San Zhang','Si Li', 'Si Li', 'Si Li', 'Si Li'],'Subject':['Chinese', 'Chinese', 'Math', 'Math','Chinese', 'Chinese', 'Math', 'Math'],'Grade':[80, 90, 100, 90, 70, 80, 85, 95]})
df
	Name		Subject	Grade
0	San Zhang	Chinese	80
1	San Zhang	Chinese	90
2	San Zhang	Math	100
3	San Zhang	Math	90
4	Si Li		Chinese	70
5	Si Li		Chinese	80
6	Si Li		Math	85
7	Si Li		Math	95

参数:

'''
    DataFrame.pivot_table(values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All', observed=False, sort=True)
    创建一个电子表格风格的数据透视表作为DataFrame,数据透视表中的层将存储在结果DataFrame的索引和列上的MultiIndex对象(分层索引)中
    参数:
        values:需要被聚合的列,可选
        
        index:column 或 Grouper 或 array 或 上个列表
            如果传入一个数组,它必须与数据的长度相同。 列表可以包含任何其他类型(
            列表除外)。 在透视表索引上进行分组的键。 如果传入一个数组,则以与列值
            相同的方式使用它

        columns:column 或 Grouper 或 array 或 上个列表
            如果传入一个数组,它必须与数据的长度相同。 列表可以包含任何其他类型(
            列表除外)。 在透视表列上进行分组的键。 如果传入一个数组,则以与列值
            相同的方式使用它

        aggfunc:function 或 list(functions) 或 dict 默认为 numpy.mean
            如果传递了函数列表,那么结果透视表将有层次结构列,它们的顶层是函数名(
            从函数对象本身推断出来)。如果传递了dict,键是column to aggregate,值
            是function或函数列表

        fill_value:标量 默认NULL
            用于替换缺失值的值(在结果数据透视表中,聚合后)

        margins:布尔值,默认为 False
            不要包括其条目全部为 NaN 的列。

        margins_name:str, 默认 ‘All’
            当边距为True时,包含总计的行/列的名称

        observed:布尔值, 默认 False
            这只适用于任何组别属于分类的情况。 
            如果为True:只显示分类组别的观察值。 
            如果为False:显示分类组别的所有值。  

        sortbool:布尔值, 默认 True
            指定结果是否应该排序
    
    return DataFrame
        一个Excel风格的数据透视表
'''

最常用的参数就是 aggfunc 指定聚合

df.pivot_table(index = 'Name',
                columns = 'Subject',
                values = 'Grade',
                aggfunc = 'mean')
Subject		Chinese	Math
Name		
San Zhang	85		95
Si Li		75		90

这里aggfunc传入的参数包含所有的聚合字符串,此外还可以传入以序列为输入 标量为输出的聚合函数来实现自定义操作

df.pivot_table(index = 'Name',
                columns = 'Subject',
                values = 'Grade',
                aggfunc = lambda x:x.mean())
Subject		Chinese	Math
Name		
San Zhang	85		95
Si Li		75		90	

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

边际汇总就是把所有值算出来之后再进行给出聚合字符串再算一遍

df.pivot_table(index = 'Name',
                columns = 'Subject',
                values = 'Grade',
                aggfunc='mean',
                margins=True)
Subject		Chinese	Math	All
Name			
San Zhang	85		95.0	90.00
Si Li		75		90.0	82.50
All			80		92.5	86.25
宽表变长表
  • melt宽表转长表

创建表以便学习

df = pd.DataFrame({'Class':[1,2],
                    'Name':['San Zhang', 'Si Li'],
                    'Chinese':[80, 90],
                    'Math':[80, 75]})
df
	Class	Name	Chinese	Math
0	1	San Zhang	80		80
1	2	Si Li		90		75

参数:

'''
    DataFrame.melt(id_vars=None, value_vars=None, var_name=None, value_name='value', col_level=None, ignore_index=True)
    将DataFrame从宽格式Unpivot到长格式,可以选择保留标识符集。  
    参数:
        id_vars:元组、列表或 ndarray,可选
            用作标识符变量的列。

        value_vars:元组、列表或 ndarray,可选
            要取消透视的列。如果未指定,则使用所有未设置为id_vars的列。

        var_name:标量
            用于“变量”列的名称。如果 None 它使用 frame.columns.name或“变量”。

        value_name:标量,默认“value”
            用于“value”列的名称。

        col_level: int 或 str,可选
            如果列是 MultiIndex,则使用此级别来融化。

        ignore_index:布尔值,默认为 True
            如果为 True,则忽略原始索引。
            如果为 False,则保留原始索引。
            索引标签将根据需要重复。

    Returns DataFrame
        没有透视DataFrame.
'''

示例:

df_melted = df.melt(id_vars = ['Class', 'Name'],#需要保留不动的列
                    value_vars = ['Chinese', 'Math'],#要动的列(把这个值翻转过来,从字段到数值)
                    var_name = 'Subject',#反转字段后的列名(字段成为值之后需要给出新字段)
                    value_name = 'Grade')#因为反转了字段,字段下面的值也有反转,所以这个参数是反转后值的字段
                    #保留不动的列根据反转的字段来复制多少个
df_melted
	Class	Name	Subject	Grade
0	1	San Zhang	Chinese	80
1	2	Si Li		Chinese	90
2	1	San Zhang	Math	80
3	2	Si Li		Math	75

meltpivot是一组互逆过程,那么就一定可以通过pivot操作把

df_melted转回df的形式

df_unmelted = df_melted.pivot(index = ['Class', 'Name'],
                                     columns='Subject',
                                     values='Grade')
df_unmelted # 下面需要恢复索引,并且重命名列索引名称
		Subject	Chinese	Math
Class	Name		
	1	San Zhang	80	80
	2	Si Li		90	75
df_unmelted = df_unmelted.reset_index().rename_axis(columns={'Subject':''})
df_unmelted.equals(df)#测试两个对象是否包含相同的元素
True

DataFrame.equals

'''
    DataFrame.equals(other)
        测试两个对象是否包含相同的元素
        此功能允许将两个 Series 或 DataFrame 相互比较,
        以查看它们是否具有相同的形状和元素。同一位置的 NaN 被认为是相等的。

        行/列索引不需要具有相同的类型,只要值被认为是相等的。对应的列必须具有相同的 dtype。
    
    参数
        other:Series 或者 DataFrame
            要与第一个进行比较的其他 Series 或 DataFrame。

    return 布尔
        如果两个对象中的所有元素都相同,则为 True,否则为 False。
'''
  • wide_to_long 宽表转长表

    创建表以便学习

df = pd.DataFrame({'Class':[1,2],'Name':['San Zhang', 'Si Li'],'Chinese_Mid':[80, 75], 'Math_Mid':[90, 85],'Chinese_Final':[80, 75], 'Math_Final':[90, 85]})
df
	Class	Name	Chinese_Mid	Math_Mid	Chinese_Final	Math_Final
0		1	San Zhang	80			90			80				90
1		2	Si Li		75			85			75				85

参数:

'''
    pandas.wide_to_long(df, stubnames, i, j, sep='', suffix='\d+')
    参数
        df:需要更改的DataFrame

        stubnames:需要拆分字段的前字段

        i:需要保留的字段

        j:需要拆分字段的后字段的列名

        sep:拆分的字段的拆分符号

        suffix:拆分符号后的正则匹配
        
'''

示例1:

pd.wide_to_long(df,
    stubnames=['Chinese', 'Math'],
    i = ['Class', 'Name'],
    j='Examination',
    sep='_',
    suffix='.+')
							Chinese	Math
Class	Name	Examination		
1	San Zhang	Mid				80	90
				Final			80	90
2	Si Li		Mid				75	85
				Final			75	85

示例2:

res = pivot_multi.copy()
res.columns = res.columns.map(lambda x:'_'.join(x))
res = res.reset_index()
res = pd.wide_to_long(res, stubnames=['Grade', 'rank'],
                        i = ['Class', 'Name'],
                        j = 'Subject_Examination',
                        sep = '_',
                        suffix = '.+')
res
										Grade	rank
Class	Name	Subject_Examination		
1	San Zhang		Chinese_Mid				80	10
                	Chinese_Final			75	15
                	Math_Mid				90	20
                	Math_Final				85	7
2	Si Li			Chinese_Mid				85	21
                	Chinese_Final			65	15
                	Math_Mid				92	6
                	Math_Final				88	2

str.join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串。

示例3:

res = res.reset_index()
res[['Subject', 'Examination']] = res[  
                'Subject_Examination'].str.split('_', expand=True)
res = res[['Class', 'Name', 'Examination',
                'Subject', 'Grade', 'rank']].sort_values('Subject')

res = res.reset_index(drop=True)
res
	Class	Name	Examination	Subject	Grade	rank
0		1	San Zhang	Mid		Chinese	80		10
1		1	San Zhang	Final	Chinese	75		15
2		2	Si Li		Mid		Chinese	85		21
3		2	Si Li		Final	Chinese	65		15
4		1	San Zhang	Mid		Math	90		20
5		1	San Zhang	Final	Math	85		7
6		2	Si Li		Mid		Math	92		6
7		2	Si Li		Final	Math	88		2

索引的变形

stack与unstack

  • stack

参数:

'''
    DataFrame.stack(level=- 1, dropna=True)
    将列中的规定层堆叠到索引。
    返回与当前 DataFrame 相比具有一个或多个新的最内层的多级索引的重构 DataFrame 或 Series。通过旋转当前数据框的列来创建新的最内层
        - 如果列有一个级别,则输出是一个系列;
        - 如果列有多个级别,则新的索引级别是从规定的级别获取的,
            并且输出是一个 DataFrame。
    参数
        level:int,str,list,默认 -1
            从列轴堆叠到索引轴的级别,定义为一个索引或标签,或者索引或标签的列表。

        dropna:布尔值,默认为 True
            是否删除结果帧/系列中缺少值的行。将列级别堆叠到索引轴上可以创建原始数据帧中缺少的索引和列值的组合。
    
    return:DataFrame or Series
'''
  • unstack

参数:

'''
	DataFrame.unstack(level=- 1, fill_value=None)
    枢轴(必须是分层的)索引标签的级别。
    返回具有新级别列标签的 DataFrame,其最内层由枢轴索引标签组成。
    参数
        level:int、str 或这些列表,默认 -1(最后一级)
            要解栈的索引级别,可以传递级别名称。

        fill_value:int、str 或 dict
            如果unstack产生缺失值,则将NaN 替换为该值。

    return:DataFrame or Series
'''

​ 之前提到了利用swaplevelreorder_levels进行索引内部的层交换,stack/unstack就是行列索引交换,由于这种交换带来了DataFrame维度上的变化,因此属于变形操作

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'])
#可能会产生缺失值
df
					col_1	col_2
A	cat		big		1.0		1.0
	dog		small	1.0		1.0
B	cat		big		1.0		1.0
	dog		small	1.0		1.0

unstack主要的参数是移动的层号默认转化最内层,移动到列索引的最内层,同时支持同时转化多个层

df.unstack(2)#将最内层转化了,这里也可以写-1,他把第三个行索引转到列索引了
		col_1			col_2
		big		small	big		small
A	cat	1.0		NaN		1.0		NaN
	dog	NaN		1.0		NaN		1.0
B	cat	1.0		NaN		1.0		NaN
	dog	NaN		1.0		NaN		1.0
df.unstack([0,2])#把第0层和第2层转到列索引(由行索引起步)
	col_1					col_2
	A			B			A			B
	big	small	big	small	big	small	big	small
cat	1.0	NaN		1.0	NaN		1.0	NaN		1.0	NaN
dog	NaN	1.0		NaN	1.0		NaN	1.0		NaN	1.0
  • 类似于pivot中的唯一性要求,在stack/unstack中必须保证被转为列索引的行索引层和被保留的行索引层构成的组合是唯一的,,破坏了唯一性,那么就会报错
my_index = df.index.to_list()
my_index[1] = my_index[0]
df.index = pd.Index(my_index)
df
				col_1	col_2
A	cat	big		1.0		1.0
		big		1.0		1.0
B	cat	big		1.0		1.0
	dog	small	1.0		1.0
try:
    df.unstack()
except Exception as e:
    Err_Msg = e
Err_Msg
ValueError('Index contains duplicate entries, cannot reshape')
  • 与unstack相反,stack的作用就是把列索引的层压入行索引,其用法完全类似
df = pd.DataFrame(np.ones((4,2)),
                    index = pd.Index([('A', 'cat', 'big'),
                                     ('A', 'dog', 'small'),
                                     ('B', 'cat', 'big'),
                                     ('B', 'dog', 'small')]),
                   columns=['index_1', 'index_2']).T
df
		A			B
		cat	dog		cat	dog
		big	small	big	small
index_1	1.0	1.0		1.0	1.0
index_2	1.0	1.0		1.0	1.0
df.stack()
				A		B
				cat	dog	cat	dog
index_1	big		1.0	NaN	1.0	NaN
		small	NaN	1.0	NaN	1.0
index_2	big		1.0	NaN	1.0	NaN
		small	NaN	1.0	NaN	1.0

unstack把行索引转到列索引 , stack把列索引转到行索引

聚合与变形的关系

​ 在上面介绍的所有函数中,除了带有聚合效果的 pivot_table 以外,所有的函数在变形前后并不会带来 values 个数的改变,只是这些值在呈现的形式上发生了变化

其他变形函数

读取表便于学习:

df = pd.read_csv('C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv')
df.head()
	School	Grade	Name	Gender	Height	Weight	Transfer	Test_Number	Test_Date	Time_Record
0	Shanghai Jiao Tong University	Freshman	Gaopeng Yang	Female	158.9	46.0	N	1	2019/10/5	0:04:34
1	Peking University	Freshman	Changqiang You	Male	166.5	70.0	N	1	2019/9/4	0:04:20
2	Shanghai Jiao Tong University	Senior	Mei Sun	Male	188.9	89.0	N	2	2019/9/12	0:05:22
3	Fudan University	Sophomore	Xiaojuan Sun	Female	NaN	41.0	N	2	2020/1/3	0:04:08
4	Fudan University	Sophomore	Gaojuan You	Male	174.0	74.0	N	2	2019/11/6	0:05:22
  • crosstab 交叉两个或多个序列成为新的表

参数:

'''
    pandas.crosstab(index, columns, values=None, rownames=None, colnames=None, aggfunc=None, margins=False, margins_name='All', dropna=True, normalize=False)
    计算两个(或更多)因子的简单交叉表。默认情况下,除非传递值数组和聚合函数,否则会计算因子的频率表。
    参数
        index:array-like, Series, or list of arrays/Series
            行中分组依据的值。

        column:array-like, Series, or list of arrays/Series
            列中分组依据的值。

        values:数组,可选
            根据因子聚合的值数组。需要指定aggfunc。

        rownames,默认无
            如果通过,则必须匹配通过的行数组数。

        colnames序列,默认无
            如果通过,则必须匹配通过的列数组的数量。

        aggfunc函数,可选
            如果指定,则还需要指定值。

        margins布尔值,默认为 False
            添加行/列边距(小计)。

        margins_name str,默认“全部”
            当边距为 True 时将包含总计的行/列的名称。

        dropna布尔值,默认为 True
            不要包括其条目全部为 NaN 的列。

        normalize bool, {'all', 'index', 'columns'} 或 {0,1},默认 False
            通过将所有值除以值的总和进行归一化。

            - 如果通过 'all' 或True,将对所有值进行标准化。
            - 如果通过“索引”将在每一行上标准化。
            - 如果通过“列”将在每一列上进行规范化。
            - 如果 margins 是True,也将规范化边距值。
'''

示例:

pd.crosstab(index = df.School, columns = df.Transfer)
Transfer						N	Y
School		
Fudan University				38	1
Peking University				28	2
Shanghai Jiao Tong University	53	0
Tsinghua University				62	4

如果给出了values参数,就必须指定aggfunc聚合参数

pd.crosstab(index = df.School, columns = df.Transfer,
            values = [0]*df.shape[0], aggfunc = 'count')#[0]*df.shape[0]只是把列表0乘以df.shape[0]就是第一维度,那这个表达式表示有第一维度的几个0
Transfer						N		Y
School		
Fudan University				38.0	1.0
Peking University				28.0	2.0
Shanghai Jiao Tong University	53.0	NaN
Tsinghua University				62.0	4.0

可以使用pivot_table进行等价操作,由于这里统计的是组合的频数,所以values参数无论传入那一个列都不会影响最后的结果

df.pivot_table(index = 'School',
                columns = 'Transfer',
                values = 'Name',
                aggfunc = 'count')
Transfer						N		Y
School		
Fudan University				38.0	1.0
Peking University				28.0	2.0
Shanghai Jiao Tong University	53.0	NaN
Tsinghua University				62.0	4.0

crosstab的对应位置传入的是具体序列,而pivot_table传入的是被调用表的对应名字,若传入序列对应的值则会报错

  • explode展开表

    ​ explode参数能够对某一列的元素进行纵向的展开,被展开的单元格必须存储为 **list, tuple, Series, np.ndarray**中的类型

创建表以便学习

df_ex = pd.DataFrame({'A': [[1, 2],'my_str',{1, 2},pd.Series([3, 4])], 'B': 1})
df_ex
	A						B
0	[1, 2]					1
1	my_str					1
2	{1, 2}					1
3	0 3 1 4 dtype: int64	1

示例1:

df_ex.explode('A')#以A列把所有的拆分
	A		B
0	1		1
0	2		1
1	my_str	1
2	1		1
2	2		1
3	3		1
3	4		1
  • get_dummies把类别特征转化为指示变量
#对年级一列转为指示变量,属于某一个年级的对应列标记为1,否则为0:
pd.get_dummies(df.Grade).head()
	Freshman	Junior	Senior	Sophomore
0		1		0		0		0
1		1		0		0		0
2		0		0		1		0
3		0		0		0		1
4		0		0		0		1

##连接

​ pandas的表连接与SQL的表连接相同,提供how参数来代表连接形式,分别为**左连接left,右连接right,内连接inner,外连接outer**

值连接

merge()

参数:

'''
    DataFrame.merge(right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=False, suffixes=('_x', '_y'), copy=True, indicator=False, validate=None)
    
    将DataFrame或命名的 Series 对象与数据库样式的连接合并。
    命名的 Series 对象被视为具有单个命名列的 DataFrame。
    连接是在 列或索引 上完成的。如果在列上连接列,DataFrame 索引将被 '忽略' 。
    否则,如果加入索引上的索引或列上的索引,则索引将被传递。执行交叉合并时,不允许合并列规范。

    warning:
        如果两个键列都包含键为空值的行,则这些行将相互匹配。这与通常的 SQL 连接行为不同,并可能导致意外结果

    参数:
        right:DataFrame 或 普通的Series

        how:连接方式{‘left’, ‘right’, ‘outer’, ‘inner’, ‘cross’}, 默认 ‘inner’
            - left:仅使用左帧中的键,类似于 SQL 左外连接;保留密钥顺序。
            - right:仅使用右框架中的键,类似于 SQL 右外连接;保留密钥顺序。
            - 外部:使用来自两个帧的键并集,类似于 SQL 完全外部联接;按字典顺序对键进行排序。
            - 内部:使用来自两个帧的键的交集,类似于 SQL 内部连接;保留左键的顺序。
            - cross:从两个帧创建笛卡尔积,保留左键的顺序
        
        on:标签 或 list
            要连接的列或索引级别名称。 这些必须在两个 DataFrame 中都找到。 
            如果on为None并且没有在索引上合并,则默认为两个DataFrames中的列的交集。

        left_on:标签 或 列表 或 数组
            行列索引层级的名称连接到左DataFrame。 也可以是一个数
            组或数组的列表长度的左DataFrame。 这些数组被当作列来处理。

        right_on:标签 或 列表 或 数组
            行列索引层级的名称连接到右DataFrame。也可以是一个数
            组或数组列表的长度为正确的DataFrame。 这些数组被当作列来处理  

        left_index:布尔型 默认False
            使用来自左DataFrame的索引作为连接键。 如果它是一个MultiIndex,
            则其他DataFrame中的键的数量(索引或列的数量)必须与级别的数量匹配

        right_index:布尔型 默认False
            使用来自右DataFrame的索引作为连接键。 与left_index相同的注意事项

        sort:布尔型 默认False
            对结果DataFrame中的联接键按字典顺序排序。 如果为False,则连接键的顺序取决于连接类型(how关键字)

        suffixes:列表 默认为 (“_x”, “_y”)
            长度为2的序列,其中每个元素都是可选的字符串,指示要分别添加到左侧和右侧重叠列名的后缀。 
            传递None值而不是字符串来指示从左到右的列名应该保持原样,不带后缀。 至少有一个值不能为None

        copy:布尔型 默认为 True
            如果为False,尽可能避免复制

        indicator:布尔型 或 str 默认值为 False
            如果为True,则在输出DataFrame中添加一个名为“_merge”的列,该列包含关于每行源的信息。 
            通过提供一个字符串参数,可以给列一个不同的名称。 该列将有一个category类型的值为“left_only”,
            用于观察其合并键只出现在左DataFrame,“right_only”用于观察其合并键只出现在右DataFrame,
            如果观察的合并键在两个DataFrame中都发现,则为“both”。 

        validate: str 可选
            如果指定,则检查merge是否为指定类型。  
                - " one_to_one "或" 1:1 ":检查合并键在左右数据集中是否唯一。  
                - " one_to_many "或" 1:m ":检查合并键是否唯一的左数据集。  
                - " many_to_one "或" m:1 ":检查合并键在右数据集中是否唯一。  
                - “many_to_many”或“m:m”:允许,但不进行检查。

    return:DataFrame
        合并了两个对象的DataFrame
'''

示例:

df1 = pd.DataFrame({'Name':['San Zhang','Si Li'],'Age':[20,30]})
df2 = pd.DataFrame({'Name':['Si Li','Wu Wang'],'Gender':['F','M']})
df1.merge(df2,on='Name',how='left')#依照Name列做左连接
	Name		Age	Gender
0	San Zhang	20	NaN
1	Si Li		30	F

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

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

出现重复列名可用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'])
	Name	Grade_Chinese	Grade_Math
0	San Zhang	70				80

如果键不是唯一的,那么结果就会产生问题。举例中的行数很少,但如果实际数据中有几十万到上百万行的进行合并时,如果想要保证唯一性,除了用 duplicated 检查是否重复外, merge 中也提供了 validate 参数来检查连接的唯一性模式。这里共有三种模式,即一对一连接 1:1 ,一对多连接 1:m ,多对一连接 m:1 连接,第一个是指左右表的键都是唯一的,后面两个分别指左表键唯一和右表键唯一

索引连接

join()

示例:

df1 = pd.DataFrame({'Age':[20,30]},index=pd.Series( ['San Zhang','Si Li'],name='Name'))
df2 = pd.DataFrame({'Gender':['F','M']},index=pd.Series(['Si Li','Wu Wang'],name='Name'))
df1.join(df2, how='left')
			Age		Gender
Name		
San Zhang	20		NaN
Si Li		30		F

方向连接

concat()

仍然关于索引连接,常用三个参数:axis,join,keys分别表示拼接方向,连接形式,以及在新表中指示来自于哪一张旧表的名字意, joinkeys 与之前提到的 join 函数和键的概念没有任何关系。

参数:

'''
    pandas.concat(objs, axis=0, join='outer', ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False, sort=False, copy=True)
    沿特定轴连接pandas对象,并沿a其他轴使用可选的设置逻辑。
    还可以在连接轴上添加一层分层索引,如果标签在传递的轴号
    上相同(或重叠),这可能很有用

    参数
        objs:Series 或 DataFrame 对象的序列或映射
            如果传递了映射,则排序的键将用作键 参数,除非传递,在这
            种情况下将选择值(见下文)。任何 None 对象都将被静默删除
            ,除非它们都是 None 在这种情况下将引发 ValueError 。

        axis:{0/'index', 1/'columns'},默认 0
            要连接的轴。

        join:{'inner', 'outer'},默认 'outer'
            如何处理其他轴(或轴)上的索引。

        ignore_index:布尔值,默认为 False
            如果为 True,则不要使用连接轴上的索引值。结果轴将标记为 0,
             ..., n - 1。如果您要连接对象,而连接轴没有有意义的索引信
             息,这将非常有用。请注意,连接中仍然尊重其他轴上的索引值。

        key:序列,默认无
            如果通过了多个级别,则应包含元组。使用传递的键作为最外层构建层次索引。

        level:列表,默认无
            用于构造 MultiIndex 的特定级别(唯一值)。否则,它们将从密钥中推断出来。

        mane:列表,默认无
            生成的分层索引中的级别名称。

        verify_integrity:bool,默认为 False
            检查新的连接轴是否包含重复项。相对于实际的数据连接,这可能非常昂贵。

        sort:布尔值,默认为 False
            如果连接 为“外部”时未对齐,则对非连接轴进行排序。当已经保留了非串联轴的顺序时,这不起作用。join='inner'
            在 1.0.0 版更改:默认情况下更改为不排序。

        copy:bool,默认 True
            如果为 False,则不要不必要地复制数据。

    return object,objects
        当沿着索引 ( axisSeries =0) 连接时, Series返回 a。当objs包含至少
        一个 DataFrame时,DataFrame返回 a。当沿列连接时(axis=1),返回DataFramea 。
'''

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

默认状态下axis=1表示横向连接多个表,常用于多个字段或特征的拼接

df1 = pd.DataFrame({'Name':['San Zhang','Si Li'],'Age':[20,30]})
df2 = pd.DataFrame({'Name':['Wu Wang'], 'Age':[40]})
pd.concat([df1, df2])
	Name		Age
0	San Zhang	20
1	Si Li		30
0	Wu Wang		40
df2 = pd.DataFrame({'Grade':[80, 90]})
df3 = pd.DataFrame({'Gender':['M', 'F']})
pd.concat([df1, df2, df3], 1)
	Name		Age		Grade	Gender
0	San Zhang	20		80		M
1	Si Li		30		90		F

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

df2 = pd.DataFrame({'Name':['Wu Wang'], 'Gender':['M']})
pd.concat([df1, df2])
	Name		Age		Gender
0	San Zhang	20.0	NaN
1	Si Li		30.0	NaN
0	Wu Wang		NaN		M
df2 = pd.DataFrame({'Grade':[80, 90]}, index=[1, 2])
pd.concat([df1, df2], 1)# 1 是axis
	Name		Age		Grade
0	San Zhang	20.0	NaN
1	Si Li		30.0	80.0
2	NaN			NaN		90.0
pd.concat([df1, df2], axis=1, join='inner')
	Name	Age	Grade
1	Si Li	30	80

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

key参数是可以标记生成表的数据来自那些表

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'])#key是表明那些数据来自那些表
		Name		Age
one	0	San Zhang	20
	1	Si Li		21
two	0	Wu Wang		21	

序列与表的合并

​ 如果想把序列追加到表的行末或者列末,可以分别使用appendassign方法

  • append

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

s = pd.Series(['Wu Wang', 21], index = df1.columns)
df1.append(s, ignore_index=True)
	Name		Age
0	San Zhang	20
1	Si Li		21
2	Wu Wang		21
  • assign

    ​ 一般通过df['new_col']=...追加新列,缺点是会直接改动原表,所以assign返回的是一个临时副本

s = pd.Series([80, 90])
df1.assign(Grade=s)#追加列
	Name		Age	Grade
0	San Zhang	20	80
1	Si Li		21	90
df1['Grade'] = s
df1
	Name		Age	Grade
0	San Zhang	20	80
1	Si Li		21	90

类连接操作

比较

compare()

​ 它能够比较两个表或者序列的不同处并进行汇总展示

df1 = pd.DataFrame({'Name':['San Zhang', 'Si Li', 'Wu Wang'],'Age':[20, 21 ,21],'Class':['one', 'two', 'three']})
df2 = pd.DataFrame({'Name':['San Zhang', 'Li Si', 'Wu Wang'],'Age':[20, 21 ,21],'Class':['one', 'two', 'Three']})
df1.compare(df2)
	Name			Class
	self	other	self	other
1	Si Li	Li Si	NaN		NaN
2	NaN		NaN		three	Three

如果相同则会被填充为缺失值nan,其中otherself分别代指传入的参数表被调用的表自身

如果想要完整显示表中的所有元素的比较情况,可以设置keep_shape=True

df1.compare(df2, keep_shape=True)
	Name			Age				Class
	self	other	self	other	self	other
0	NaN		NaN		NaN		NaN		NaN		NaN
1	Si Li	Li Si	NaN		NaN		NaN		NaN
2	NaN		NaN		NaN		NaN		three	Three

组合

combine

​ 能够让两张表按照一定规则进行组合,在进行规则比较的时候列索引会自动对其

def choose_min(s1, s2):
    s2 = s2.reindex_like(s1)
    res = s1.where(s1<s2, s2)
    res = res.mask(s1.isna()) # isna表示是否为缺失值,返回布尔序列
    return res

df1 = pd.DataFrame({'A':[1,2], 'B':[3,4], 'C':[5,6]})
df2 = pd.DataFrame({'B':[5,6], 'C':[7,8], 'D':[9,10]}, index=[1,2])
df1.combine(df2, choose_min)
	A	B	C	D
0	NaN	NaN	NaN	NaN
1	NaN	4.0	6.0	NaN
2	NaN	NaN	NaN	NaN

DataFrame.mask() 替换条件为 True 的值

overtwrite参数为False可以保留被调用表中未出现在传入的参数表中的列,而不会设置为缺失值

df1.combine(df2, choose_min, overwrite=False)#A列是没有进行比较的
	A	B	C	D
0	1.0	NaN	NaN	NaN
1	2.0	4.0	6.0	NaN
2	NaN	NaN	NaN	NaN

combine_first方法,在对两张表组合时,若第二张表中的值在第一张表中对应索引位置的值不是缺失状态,那么就使用第一张表的值填充

df1 = pd.DataFrame({'A':[1,2], 'B':[3,np.nan]})
df2 = pd.DataFrame({'A':[5,6], 'B':[7,8]}, index=[1,2])
df1.combine_first(df2)
	A	B
0	1	3.0
1	2	7.0
2	6	8.0

缺失值

###统计

  • isnaisnull

    ​ 查看每个单元格是否缺失

读取表以便学习

df = pd.read_csv('C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv',
        usecols = ['Grade', 'Name', 'Gender', 'Height','Weight', 'Transfer'])
df.isna().head()
	Grade	Name	Gender	Height	Weight	Transfer
0	False	False	False	False	False	False
1	False	False	False	False	False	False
2	False	False	False	False	False	False
3	False	False	False	True	False	False
4	False	False	False	False	False	False

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

df[df.Height.isna()].head()
	Grade		Name			Gender	Height	Weight	Transfer
3	Sophomore	Xiaojuan Sun	Female	NaN		41.0	N
12	Senior		Peng You		Female	NaN		48.0	NaN
26	Junior		Yanli You		Female	NaN		48.0	N
36	Freshman	Xiaojuan Qin	Male	NaN		79.0	Y
60	Freshman	Yanpeng Lv		Male	NaN		65.0	N

同时对几个列检查缺失情况可以用isna,notnaany,all的组合

'''
	any()一个序列中满足一个True,则返回True;
    all()一个序列中所有值为True时,返回True,否则为False
'''

参数:

'''
    DataFrame.any(self, axis=0, bool_only=None, skipna=True, level=None, **kwargs)
        DataFrame.all(self, axis=0, bool_only=None, skipna=True, level=None, **kwargs)
        参数
            参数
            axis:{0 或 'index',1 或 'columns',无},默认 0
                指示应减少哪个轴或多个轴。
                    0 / 'index' : 减少索引,返回一个以原始列标签为索引的系列。
                    1 / 'columns' :减少列,返回一个索引为原始索引的系列。
                    None:减少所有轴,返回一个标量。
            bool_only:布尔值,默认None
                仅包括布尔列。如果没有,将尝试使用所有内容,然后仅使用布尔数据。未针对系列实施。

            skipna:布尔值,默认为 True
                排除 NA/空值。如果整个行/列为 NA 且 skipna 为 True,则结果将为 False,与空行/列一样。
                如果 skipna 为 False,则 NA 被视为 True,因为它们不等于零。

            level: int 或 层级名称,默认无
                如果轴是 MultiIndex(分层),则沿特定级别计数,折叠成一个系列。

        Returns:Series or DataFrame
            如果指定了level,则返回DataFrame;否则,返回Series
'''

示例1:

sub_set = df[['Height', 'Weight', 'Transfer']]
df[sub_set.isna().all(1)] # 全部缺失
	Grade	Name			Gender	Height	Weight	Transfer
102	Junior	Chengli Zhao	Male	NaN		NaN		NaN

示例2:

df[sub_set.isna().any(1)].head() # 至少有一个缺失
	Grade		Name			Gender	Height	Weight	Transfer
3	Sophomore	Xiaojuan Sun	Female	NaN		41.0	N
9	Junior		Juan Xu			Female	164.8	NaN		N
12	Senior		Peng You		Female	NaN		48.0	NaN
21	Senior		Xiaopeng Shen	Male	166.0	62.0	NaN
26	Junior		Yanli You		Female	NaN		48.0	N

示例3:

df[sub_set.notna().all(1)].head() # 没有缺失
	Grade		Name			Gender	Height	Weight	Transfer
0	Freshman	Gaopeng Yang	Female	158.9	46.0	N
1	Freshman	Changqiang You	Male	166.5	70.0	N
2	Senior		Mei Sun			Male	188.9	89.0	N
4	Sophomore	Gaojuan You		Male	174.0	74.0	N
5	Freshman	Xiaoli Qian		Female	158.0	51.0	N

删除

  • dropna

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

参数:

'''
    DataFrame.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)
    删除缺失值。
    参数
        axis:{0 或 'index',1 或 'columns'},默认 0
            确定是否删除了包含缺失值的行或列。
                0 或 'index' :删除包含缺失值的行。
                1 或 'columns' :删除包含缺失值的列。
                (在 1.0.0 版更改:将元组或列表传递到多个轴上。只允许使用单个轴。)

        how:{'any', 'all'}, 默认 'any'
        当我们至少有一个 NA 或全部 NA 时,确定是否从 DataFrame 中删除行或列。
            'any' :如果存在任何 NA 值,则删除该行或列。
            'all' :如果所有值都是 NA,则删除该行或列。

        thresh: int,可选
            删除的非缺失值个数阈值,非缺失值 没有达到这个数量的相应维度会被删除
            其实就是删除条件

        subset:列标签或标签序列,可选
            要考虑的沿其他轴的标签,例如,如果您要删除行,这些将是要包含的列列表。

        inplace:bool,默认 False
            如果为 True,则在原地执行操作并返回 None。
'''

示例1:

res = df.dropna(how = 'any', subset = ['Height', 'Weight'])
res.shape
(174, 6)

示例2:

res = df.dropna(1, thresh=df.shape[0]-15) # 身高被删除
res.head()
#没有达到thresh的条件
	Grade		Name			Gender	Weight	Transfer
0	Freshman	Gaopeng Yang	Female	46.0	N
1	Freshman	Changqiang You	Male	70.0	N
2	Senior		Mei Sun			Male	89.0	N
3	Sophomore	Xiaojuan Sun	Female	41.0	N
4	Sophomore	Gaojuan You		Male	74.0	N

不用dropna 也可以用布尔索引完成

res = df.loc[df[['Height', 'Weight']].notna().all(1)]
res.shape
(174, 6)
res = df.loc[:, ~(df.isna().sum()>15)]
res.head()
	Grade		Name			Gender	Weight	Transfer
0	Freshman	Gaopeng Yang	Female	46.0	N
1	Freshman	Changqiang You	Male	70.0	N
2	Senior		Mei Sun			Male	89.0	N
3	Sophomore	Xiaojuan Sun	Female	41.0	N
4	Sophomore	Gaojuan You		Male	74.0	N

###填充

  • fillna

    ​ 最常用三个参数:value,method,limit

    ​ 其中value为填充值,可以是标量,也可以是索引到元素的字典映射

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

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

参数:

'''
    DataFrame.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None)
    使用指定的方法填充 NA/NaN 值
    参数
        value:标量、dict、Series 或 DataFrame
            用于填充孔的值(例如 0),或者是值的 dict/Series/DataFrame,指定每个索引(对于 Series)或列(对于 DataFrame)使用哪个值。
            不在 dict/Series/DataFrame 中的值将不会被填充。'此值不能是列表'。

        method:{'backfill', 'bfill', 'pad', 'ffill', None},默认None
            用于填充重新索引系列垫/填充中的孔的方法:
                将最后一个有效观察向前传播到下一个有效回填/bfill:使用下一个有效观察来填充间隙。

        axis:{0 或“索引”、1 或“列”}
            沿其填充缺失值的轴。

        inplace:bool,默认 False
            如果为真,则就地填写。
                注意:这将修改此对象上的任何其他视图(例如,DataFrame 中列的无复制切片)。

        limit: int 默认None
            如果指定了方法,则这是向前/向后填充的连续 NaN 值的最大数量。
            换句话说,如果有超过这个数量的连续 NaN 的间隙,它只会被部分填充。
            如果未指定方法,则这是沿整个轴将填充 NaN 的最大条目数。如果不是无,则必须大于 0。

        downcast:dict,默认为 None
            一个 item->dtype 的字典,如果可能的话,或者字符串 'infer',
            它将尝试向下转换为适当的相等类型(例如,如果可能,float64 到 int64)
'''

创建表以便学习

s = pd.Series([np.nan, 1, np.nan, np.nan, 2, np.nan],list('aaabcd'))
s

示例1:

s.fillna(method='ffill') # 用前面的值向后填充
a    NaN
a    1.0
a    1.0
b    1.0
c    2.0
d    2.0
dtype: float64

示例2:

s.fillna(method='ffill', limit=1) # 连续出现的缺失,最多填充一次
a    NaN
a    1.0
a    1.0
b    NaN
c    2.0
d    2.0
dtype: float64

示例3:

s.fillna(s.mean()) # value为标量
a    1.5
a    1.0
a    1.5
b    1.5
c    2.0
d    1.5
dtype: float64

示例4:

s.fillna({'a': 100, 'd': 200}) # 通过索引映射填充的值
a    100.0
a      1.0
a    100.0
b      NaN
c      2.0
d    200.0
dtype: float64

有时为了更加合理地填充,需要先进行分组后再操作

df.groupby('Grade')['Height'].transform(lambda x: x.fillna(x.mean())).head()
0    158.900000
1    166.500000
2    188.900000
3    163.075862
4    174.000000
Name: Height, dtype: float64

插值

​ 对于 interpolate 而言,除了插值方法(默认为 linear 线性插值)之外,有与 fillna 类似的两个常用参数,一个是控制方向的 limit_direction ,另一个是控制最大连续缺失值插值个数的 limit 。其中,限制插值的方向默认为 forward ,这与 fillna 的 method 中的 ffill 是类似的,若想要后向限制插值或者双向限制插值可以指定为 backward 或 both 。

Nullable类型

####基本概念

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

print(None == None)
None == False
True
False

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

np.nan == np.nan
False

注意:虽然在对缺失序列或表格的元素进行比较操作的时候np.nan的对应位置会返回**False,但是在使用equals函数进行两张表或两个序列的相同性检验时,会自动跳过两侧表都是缺失值的位置**,直接返回**True**

'''
    DataFrame.equals(other)
    测试两个对象是否包含相同的元素。
    此功能允许将两个 Series 或 DataFrame 相互比较,以查看它们是否具
    有相同的形状和元素。同一位置的 NaN 被认为是相等的。
    行/列索引不需要具有相同的类型,只要值被认为是相等的。对应的列必须
    参数
        other:Series或DataFrame
            要与第一个进行比较的其他 Series 或 DataFrame。具有相同的 dtype。

    return:bool
        如果两个对象中的所有元素都相同,则为 True,否则为 False
'''

示例1:

s1 = pd.Series([1, np.nan])
s2 = pd.Series([1, 2])
s3 = pd.Series([1, np.nan])
s1 == 1
0     True
1    False
dtype: bool

示例2:

s1.equals(s2)
False

示例3:

s1.equals(s3)
True

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

pd.to_timedelta(['30s', np.nan]) # Timedelta中的NaT
TimedeltaIndex(['0 days 00:00:30', NaT], dtype='timedelta64[ns]', freq=None)
pd.to_datetime(['20200101', np.nan]) # Datetime中的NaT
DatetimeIndex(['2020-01-01', 'NaT'], dtype='datetime64[ns]', freq=None)

​ 在 pandas 中可以看到 object 类型的对象,而 object 是一种混杂对象类型,如果出现了多个类型的元素同时存储在 Series 中,它的类型就会变成 object

NaT的问题根源在于np.nan本身是一种浮点型,如果浮点型和时间类型混合存储,就会成为object

​ 因此,pandas尝试设计了一种新的缺失类型pd.NA以及三种Nullable序列类型来应对,它们分别是Int,boolean和strin

性质

​ Nullable性质就是序列类型不受缺失值影响的类型

​ 在**Int,boolean和strin中存储缺失值**,pandas都会转为内置的pd.NA

示例1:

pd.Series([np.nan, 1], dtype = 'Int64') # "i"是大写的
0    <NA>
1       1
dtype: Int64

示例2:

pd.Series([np.nan, True], dtype = 'boolean')
0    <NA>
1    True
dtype: boolean

示例3:

pd.Series([np.nan, 'my_str'], dtype = 'string')
0      <NA>
1    my_str
dtype: string

如果在Int类型序列乘以小数,只会是浮点型

  • pandas的boolean类型 与 python的bool类型的区别

1 . 带有缺失值的布尔列表无法进行索引器选择,,而**boolean**会把缺失值看作False

s = pd.Series(['a', 'b'])
s_bool = pd.Series([True, np.nan])#不会把np.nan看成False+
s_boolean = pd.Series([True, np.nan]).astype('boolean')
# s[s_bool] # 报错
s[s_boolean]
0    a
dtype: object

2 . 在逻辑运算的时候bool类型在缺失处返回的永远是False,而boolean会根据逻辑运算是否能确定唯一结果来返回相应的值

s_boolean & True
0    True
1    <NA>
dtype: boolean
s_boolean | True
0    True
1    True
dtype: boolean
~s_boolean # 取反操作同样是无法唯一地判断缺失结果
0    False
1     <NA>
dtype: boolean
  • 一般在实际数据处理时,可以在数据集读入后,先通过convert_dtypes转为Nullable类型
df = pd.read_csv('C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv')
df = df.convert_dtypes()
df.dtypes
School          string
Grade           string
Name            string
Gender          string
Height         Float64
Weight           Int64
Transfer        string
Test_Number      Int64
Test_Date       string
Time_Record     string
dtype: object

计算和分组

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

s = pd.Series([2,3,np.nan,4,5])
s.sum()
14.0
s.prod()
120.0

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

s.cumsum()
0     2.0
1     5.0
2     NaN
3     9.0
4    14.0
dtype: float64

​ 除了np.nan**0np.nan**1 这两种情况为确定值之外(结果为1.0),所有运算返回结果全为缺失(pd.NA的行为与此一致),并且np.nan比较操作时一定返回False,而**pd.NA返回pd.NA**

diff与pct_change

​ 前者凡是缺失计算都设置为缺失值,后者缺失位置会被设置为0%的变化率

  • diff
s.diff()
0    NaN
1    1.0
2    NaN
3    NaN
4    1.0
dtype: float64
  • pct_change()
s.pct_change()
0         NaN
1    0.500000
2    0.000000
3    0.333333
4    0.250000
dtype: float64

缺失值分组

​ 缺失值可以作为一个类别来处理,在分组(groupby)里面的**get_dummies函数来增加缺失类别**

创建表便于学习

df_nan = pd.DataFrame({'category':['a','a','b',np.nan,np.nan],'value':[1,3,5,7,9]})
df_nan
	category	value
0		a		1
1		a		3
2		b		5
3		NaN		7
4		NaN		9

示例:

#先分组
df_nan.groupby('category',dropna=False)['value'].mean() # pandas版本大于1.1.0
category
a      2.0
b      5.0
NaN    8.0
Name: value, dtype: float64
pd.get_dummies(df_nan.category, dummy_na=True)
	a	b	NaN
0	1	0	0
1	1	0	0
2	0	1	0
3	0	0	1
4	0	0	1

文本数据

str对象

str 对象是定义在 Index 或 Series 上的属性,与python中的str模块类似

var = 'abcd'
str.upper(var) # Python内置str模块
'ABCD'
s = pd.Series(['abcd', 'efg', 'hi'])
s.str.upper() # pandas中str对象上的upper方法
0    ABCD
1     EFG
2      HI
dtype: object

###string类型

文本处理操作

拆分

str.split()

​ 第一个参数为**正则表达式,第二个参数为从左到右最大拆分**字数n,是否展开为多个列expand

s = pd.Series(['上海市黄浦区方浜中路249号','上海市宝山区密山路5号'])
s.str.split('[市区路]')#正则表达式
0   	[上海, 黄浦, 方浜中, 249]
1       [上海, 宝山, 密山, 5]
dtype: object
s.str.split('[市区路]', n=2, expand=True)#n最大拆分次数,expend是否展开多个列
	0	 1	   2
0	上海	黄浦	方浜中路2491	上海	宝山	密山路5

合并

str.join()

​ 用某个链接把Series中的字符串列表连起来,列表中出现了非字符串元素则返回缺失值

s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
s.str.join('-')#因为出现非字符串元素,所以返回了缺失值
0    a-b
1    NaN
2    NaN
dtype: object

str.cat()

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

s1 = pd.Series(['a','b'])
s2 = pd.Series(['cat','dog'])
s1.str.cat(s2,sep='-')
0    a-cat
1    b-dog
dtype: object
s2.index = [1, 2]
s1.str.cat(s2, sep='-', na_rep='?', join='outer')#na_rep缺失值代替符号
0      a-?
1    b-cat
2    ?-dog
dtype: object

匹配

str.contains()

​ 返回每个字符串是否符合正则表达式的布尔索引

s = pd.Series(['my cat', 'he is fat', 'railway station'])
s.str.contains('swat')#给出正则模式,查找包含了这个正则的字符串元素,如果找得到就返回True
0     True
1     True
2    False
dtype: bool

str.startswith()返回以给定模式开始布尔序列

str.endwith() 返回以给定模式结束布尔序列

他们都不支持正则表达式

s.str.startswith('my')#指定开始字符
0     True
1    False
2    False
dtype: bool
s.str.endswith('t')#指定结束字符
0     True
1     True
2    False
dtype: bool

str.match()返回每组字符串是否符合正则表达式布尔序列

s.str.match('m|h')
0     True
1     True
2    False
dtype: bool
s.str[::-1].str.match('ta[f|g]|n') # 反转后匹配
0    False
1     True
2     True
dtype: bool

当然,这些也能通过在 str.contains 的正则中使用 ^ 和 $ 来实现

s.str.contains('^[m|h]')
0     True
1     True
2    False
dtype: bool

返回索引的匹配函数

str.find() 返回从左到右第一次匹配的位置索引,未找到则返回-1

str.rfind() 返回从右到左第一次匹配的位置索引,未找到则返回-1

s = pd.Series(['This is an apple. That is not an apple.'])
s.str.find('apple')
0    11
dtype: int64
s.str.rfind('apple')
0    33
dtype: int64

替换

str.replace()

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

参数:

'''
    Series.str.replace(pat, repl, n=- 1, case=None, flags=0, regex=None)
    替换系列/索引中每次出现的模式/正则表达式
    参数
        pat:str或编译的正则表达式
            字符串可以是字符序列或正则表达式。

        repl:str或可调用
            替换ment字符串或可调用对象。可调用对象传递正则表达式匹配对象,
            并且必须返回要使用的替换字符串。见re.sub()。

        n:int,默认 -1(全部)
            从一开始就进行的更换次数。

        case:布尔值,默认无
            确定替换是否区分大小写:
                如果为 True,则区分大小写(如果pat是字符串,则为默认值)
                设置为 False 不区分大小写
                如果pat是已编译的正则表达式,则无法设置。

        flags:int,默认 0(无标志)
            正则表达式模块标志,例如 re.IGNORECASE。如果pat是已编译的正则表达式,则无法设置。

        regex:bool,默认 True
            确定传入的模式是否是正则表达式:
                如果为 True,则假定传入的模式是正则表达式。
                如果为False ,则将模式视为文字字符串
                如果pat是已编译的正则表达式或repl是可调用的,则不能设置为 False 。
'''
s = pd.Series(['a_1_b','c_?'])
s.str.replace('d|?', 'new', regex=True)#regex是否使用正则表达式
0    a_new_b
1      c_new
dtype: object
  • 当需要对不同部分进行有差别的替换时,用子组的方法可以通过传入自定义的替换函数来分别处理字符串

注意:group(k)代表匹配到第k个子组

创建表便于学习

s = pd.Series(['上海市黄浦区方浜中路249号',
                '上海市宝山区密山路5号',
                '北京市昌平区北农路2号'])

pat = '(w+市)(w+区)(w+路)(d+号)'

city = {'上海市': 'Shanghai', '北京市': 'Beijing'}

district = {'昌平区': 'CP District',
            '黄浦区': 'HP District',
            '宝山区': 'BS District'}

road = {'方浜中路': 'Mid Fangbin Road',
        '密山路': 'Mishan Road',
        '北农路': 'Beinong Road'}

创建字符串替换函数:

#创建字符替换函数
def my_func(m):
    str_city = city[m.group(1)]
    str_district = district[m.group(2)]
    str_road = road[m.group(3)]
    str_no = 'No. ' + m.group(4)[:-1]
    return ' '.join([str_city,
                    str_district,
                    str_road,
                    str_no])
s.str.replace(pat, my_func, regex=True)
0    Shanghai HP District Mid Fangbin Road No. 249
1           Shanghai BS District Mishan Road No. 5
2           Beijing CP District Beinong Road No. 2
dtype: object
  • 可以使用 命名子组 更加清晰地写出子组代表的含义
#创建正则表达式并给每个组命名
pat = '(?P<市名>w+市)(?P<区名>w+区)(?P<路名>w+路)(?P<编号>d+号)'
#<>中间的就是组的名字

def my_func(m):
    str_city = city[m.group('市名')]
    str_district = district[m.group('区名')]
    str_road = road[m.group('路名')]
    str_no = 'No. ' + m.group('编号')[:-1]
    return ' '.join([str_city,
                    str_district,
                    str_road,
                    str_no])

s.str.replace(pat, my_func, regex=True)
0    Shanghai HP District Mid Fangbin Road No. 249
1           Shanghai BS District Mishan Road No. 5
2           Beijing CP District Beinong Road No. 2
dtype: object

提取

str.extract(regex)

​ 用正则表达式提取字符串(只匹配一个)

pat = '(w+市)(w+区)(w+路)(d+号)'
s.str.extract(pat)
	0		1		2		3
0	上海市	黄浦区	方浜中路	2491	上海市	宝山区	密山路		52	北京市	昌平区	北农路		2
  • 通过子组的命名,可以直接对新生成 DataFrame 的列命名
pat = '(?P<市名>w+市)(?P<区名>w+区)(?P<路名>w+路)(?P<编号>d+号)'
s.str.extract(pat)
	市名	区名		路名		编号
0	上海市	黄浦区	方浜中路	2491	上海市	宝山区	密山路		52	北京市	昌平区	北农路		2

str.extractall()

​ 它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储

s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
pat = '[A|B](d+)[T|S](d+)'
s.str.extractall(pat)
			0	1
	match		
my_A	0	135	15
		1	26	5
my_B	0	674	2
		1	25	6
pat_with_name = '[A|B](?P<name1>d+)[T|S](?P<name2>d+)'
s.str.extractall(pat_with_name)
			0	1
	match		
my_A	0	135	15
		1	26	5
my_B	0	674	2	
		1	25	6

str.findall()

​ 也是把所有符合条件的模式全部匹配出来,但是它把结果存入列表

s.str.findall(pat)
my_A    [(135, 15), (26, 5)]
my_B     [(674, 2), (25, 6)]
dtype: object

常用字符串函数

字符串函数

  • str.upper(小写变大写)
  • str.lower(大写变小写)
  • str.swapcase(小写变大写,大写变小写)
  • str.capitalize(句首大写,其余都小写)
  • str.title(单词首字母大写,其余都小写)

数值型函数

pd.to_numeric

对字符格式的数值进行快速转换和筛选,其主要参数包括 errorsdowncast 分别代表了非数的处理模式转换类型。其中,对于不能转换为数值的有三种 errors 选项, raise, coerce, ignore 分别表示直接报错设为缺失以及保持原来的字符串

s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])
pd.to_numeric(s, errors='ignore')
0       1
1     2.2
2      2e
3      ??
4    -2.1
5       0
dtype: object
pd.to_numeric(s, errors='coerce')
0    1.0
1    2.2
2    NaN
3    NaN
4   -2.1
5    0.0
dtype: float64
  • 数据清洗的时候,可以利用**coerce快速查看非数值型的行**
s[pd.to_numeric(s, errors='coerce').isna()]
2    2e
3    ??
dtype: object

统计型函数

countlen 的作用分别是返回出现正则表达式匹配的次数字符串的长度

s = pd.Series(['cat rat fat at', 'get feed sheet heat'])
s.str.count('[r|f]at|ee')
0    2
1    2
dtype: int64
s.str.len()
0    14
1    19
dtype: int64

格式型函数

  • 除空型

    strip() 去除两侧空格

    rstrip() 去除右侧空格

    lstrip() 去除左侧空格

  • 填充型

    pad()

    ​ 可以选定字符串长度填充的方向填充内容

s = pd.Series(['a','b','c'])
s.str.pad(5,'left','*')
0    ****a
1    ****b
2    ****c
dtype: object

left,right,bothboth是两边一起填充

  • 填充型的pad()可以用rjust,ljust,center来等效完成

ljust右填充rjust左填充

  • 在读取 excel 文件时,经常会出现数字前补0的需求除了可以使用上面的左侧填充函数进行操作之外,还可用 zfill 来实现

例如证券代码读入的时候会把”000007”作为数值7来处理

s = pd.Series([7, 155, 303000]).astype('string')
s.str.zfill(6)
0    000007
1    000155
2    303000
dtype: string

分类数据

cat对象

分类类型category

​ 可以用astype()方法直接转为category类型

读取表格便于学习

df = pd.read_csv('C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv',
    usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])
s = df.Grade.astype('category')
s.head()
0     Freshman
1     Freshman
2       Senior
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']

分类类型中定义了cat对象,与str类似,可以使用属性和方法

s.cat 
<pandas.core.arrays.categorical.CategoricalAccessor object at 0x000001C2A8498970>

​ 分类类型具有两个部分

​ 1 . categories 类别本身(以Index类型存储)

​ 2 . ordered 是否有序

s.cat.categories#类别本身
Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')
s.cat.ordered#是否有序
False

codes用来访问类别被赋予的唯一整数编号

s.cat.codes.head()
0    0
1    0
2    2
3    3
4    3
dtype: int8

####增,删,改

注意:Index 类型是无法用 index_obj[0] = item 来修改的

  • add_categories 类别的增加
s = s.cat.add_categories('Graduate') # 增加一个毕业生类别
s.cat.categories
Index(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
  • remove_categories删除类别

    ​ 原来的序列中该类会被设置为缺失

s = s.cat.remove_categories('Freshman')
s.cat.categories
Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
s.head()
0          NaN
1          NaN
2       Senior
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
  • set_categories设置序列的类别

    ​ 原先的类别会被覆盖,不满足就会设置为缺失

s = s.cat.set_categories(['Sophomore','PhD']) # 新类别为大二学生和博士
s.cat.categories
Index(['Sophomore', 'PhD'], dtype='object')
s.head()
0          NaN
1          NaN
2          NaN
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (2, object): ['Sophomore', 'PhD']
  • remove_unused_categories删除未出现在序列中的类别
s = s.cat.remove_unused_categories() # 移除了未出现的博士生类别
s.cat.categories#序列中只有Sophomore
Index(['Sophomore'], dtype='object')
  • rename_categories更改类别

    ​ 以字典的方式更改类别

    ​ 这个方法会对原序列的对应值也进行相应修改

s = s.cat.rename_categories({'Sophomore':'本科二年级学生'})
s.head()
0        NaN
1        NaN
2        NaN
3    本科二年级学生
4    本科二年级学生
Name: Grade, dtype: category
Categories (1, object): ['本科二年级学生']

###有序分类

as_unordered 有序类别

reorder_categories 无序类别

无序类别:

​ 传入的参数必须是由当前序列的无序类别构成的列表,不能够增加新的类别,也不能缺少原来的类别,并且**必须指定参数 ordered=True **,否则方法无效

s = df.Grade.astype('category')
s = s.cat.reorder_categories(['Freshman', 'Sophomore','Junior', 'Senior'],ordered=True)#无序
s.head()
0     Freshman
1     Freshman
2       Senior
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']
s.cat.as_unordered().head()
0     Freshman
1     Freshman
2       Senior
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']

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

排序和比较

​ 只需把列的类型修改为 category 后,再赋予相应的大小关系,就能正常地使用 sort_indexsort_values 进行排序

==, != , >,>=,<,<= 都能进行比较,返回布尔型

区间类别

cut/qcut

cut

参数:

'''
    pandas.cut(x, bins, right=True, labels=None, retbins=False, precision=3, include_lowest=False, duplicates='raise', ordered=True)
    将值分箱到离散区间中
    参数
        x:array-like
            要装箱的输入数组。必须是一维的。

        bins:int、sequence of scalars(标量序列) 或 IntervalIndex
        要装箱依据的条件。
            int :定义 x 范围内等宽条柱的数量。x 的范围在每侧扩展 0.1%,以包括 x 的最小值和最大值。
            sequence of scalars(标量序列) :定义允许非均匀宽度的条柱边。不扩展 x 的范围。
            IntervalIndex(间隔索引) :定义要使用的确切条柱。请注意,条柱的间隔索引必须是非重叠的。

        right:布尔型 默认为 True
            指示条柱是否包含最右边。如果(默认值),则条柱指示 (1,2]、(2,3]、(3,4)。当条柱是
            间隔索引时,将忽略此参数。right == True[1, 2, 3, 4]

        labels:array 或 False,默认值"None"
            指定返回的条柱的标签。长度必须与生成的条柱相同。如果为 False,则仅返回条柱的整数指示符。
            这会影响输出容器的类型(见下文)。当条柱是间隔索引时,将忽略此参数。如果为 True,则引发错误。当排序=False时,必须提供标签。

        retbins:bool 默认 False
            是否退回垃圾箱。当箱作为标量提供时很有用。

        precision:int 默认值 3
            存储和显示库标签的精度。

        include_lowest:布尔型,默认为 False
            第一个间隔是否应为左包含区间。

        duplicates:{默认 ‘raise’, ‘drop’},可选
            如果条柱边不唯一,请提高 ValueError 或删除非唯一边。

        ordered:布尔,默认为 True
            标签是否订购。适用于返回的类型"分类"和"系列"(使用分类 dtype)。如果为 True,则将对生成的
            分类进行排序。如果为 False,则生成的分类将是无序的(必须提供标签)。


    返回
        out:Categoryial, Series, or ndarray
            一个类似数组的对象,表示 x 的每个值的相应 bin。类型取决于标签的值。
            None(默认值):为系列 x 返回一个系列,为所有其他输入返回一个分类。其中存储的值为间隔 dtype。
            标量序列 :返回序列 x 的序列或所有其他输入的类别。其中存储的值是序列中的任何类型。
            False :返回整数的 ndarray。

        bins:numpy.ndarray 或 IntervalIndex.
            计算的或指定的条柱。仅当 retbins=True 时才返回。对于标量或序列条柱,这是一个包含计算箱的 ndarray。
            如果设置重复项 = 丢弃,则条柱将丢弃非唯一条柱。对于间隔索引条柱,这等于条柱。
'''

​ 最重要的参数是 bins,传入整数 n 代表把整个传入数组的按照最大和最小值等间距地分为 n

区间默认是左开右闭,需要在调整时把最小值包含进去

​ pandas中的解决方案是在值最小的区间左端点减去**0.001*(max-min)**

​ 指定左闭右开时,需要把 right 参数设置为 False ,相应的区间调整方法是在值最大的区间右端点加上 **0.001*(max-min) **

示例1:

s = pd.Series([1,2])
pd.cut(s, bins=2)
'''
	因为要包含最小值1,所以pandas做了 1-0.001*(2-1) 这个运算(1是左端点)
'''
0    (0.999, 1.5]
1      (1.5, 2.0]
dtype: category
Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]

示例2:

pd.cut(s, bins=2, right=False)#right左闭右开
'''
	因为要包含最大值2,所以pandas做了 2+0.001*(2-1) 这个运算(2是右端点)
'''
0      [1.0, 1.5)
1    [1.5, 2.001)
dtype: category
Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]
  • 指定区间分割点

    ​ 给bins一个列表np.infty表示无穷大

pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
0    (-inf, 1.2]
1     (1.8, 2.2]
dtype: category
Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
  • 分割点常用参数

    labels:指定区间名字

    retbins:是否返回分割点(默认False)

    ​ 指定True之后会返回两个对象,第一个是区间分区,第二个是分割点数组,可以用索引访问

s = pd.Series([1,2])
res = pd.cut(s, bins=2, labels=['small', 'big'], retbins=True)
res[0]
0    small
1      big
dtype: category
Categories (2, object): ['small' < 'big']
res[1] # 该元素为返回的分割点
array([0.999, 1.5  , 2.   ])

qcut

参数:

'''
	pandas.qcut(x, q, labels=None, retbins=False, precision=3, duplicates='raise')
	基于分位数的离散化函数。
	根据排名或基于样本分位数将变量离散化为大小相等的桶
	
	参数:
		 x:array-like
            要装箱的输入数组。必须是一维的。
            
         q:int 或 list(float)
			分位数。10 用于十分位数,4 用于四分位数等。交替排列的分位数,例如 [0, .25, .5, .75, 1.] 用于四分位数。
            
         labels:array或False,默认None
			用作结果箱的标签。必须与生成的 bin 长度相同。如果为 False,则仅返回 bin 的整数指示符。如果为 True,则引发错误。
			
		 retbins:bool 默认 False
            是否退回垃圾箱。当箱作为标量提供时很有用。
            
         precision:int 默认 3
            存储和显示库标签的精度。
            
         duplicates:{默认 ‘raise’, ‘drop’},可选
            如果条柱边不唯一,请提高 ValueError 或删除非唯一边
            
     return:
     	out:如果标签为 False,则输出分类或系列或整数数组返回类型(分类或系列)取决于输入:如果输入是系列,则为类别类型系列,否则为分类。当返回分类数据时,bin 表示为类别。

		bins:浮点数的ndarray
			仅当retbins为 True 时才返回
'''

qcut只是把bins参数改成qq参数是指quantile,这里的 q整数 n 时,指按照 n 等分位数把数据分箱,还可以传入浮点列表指代相应的分位数分割点

s = df.Weight
pd.qcut(s, q=[0,0.2,0.8,1]).head()
0      (44.0, 69.4]
1      (69.4, 89.0]
2      (69.4, 89.0]
3    (33.999, 44.0]
4      (69.4, 89.0]
Name: Weight, dtype: category
Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]

####区间构造

​ 三要素:左端点,右端点,开闭状态

开闭状态可以指定 right, left, both, neither 中的一类

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

函数:pd.Interval()

​ 此函数创建的对象属性:

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

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

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

  • pd.IntervalIndex对象

    **from_breaks**传入列表,每个元素依次为左右端点

pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')
IntervalIndex([[1, 3], [3, 6], [6, 10]],
              closed='both',
              dtype='interval[int64]')

from_arrays传入两个列表,两个列表的元素为对应形式成立区间

pd.IntervalIndex.from_arrays(left = [1,3,6,10],
                             right = [5,4,9,11],
                             closed = 'neither')
IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
              closed='neither',
              dtype='interval[int64]')

​ **from_tuples**传入元组的列表,每个元组为一个区间

pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)],
                             closed='neither')
IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
              closed='neither',
              dtype='interval[int64]')

interval_range生成等差区间

​ 参数:start, end, periods, freq 起点、终点、区间个数和区间长度

pd.interval_range(start=1,end=5,periods=8)
IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],
              closed='right',
              dtype='interval[float64]')
pd.interval_range(end=5,periods=8,freq=0.5)
IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],
              closed='right',
              dtype='interval[float64]')
  • Interval 类型的列表组成传入其中转为区间索引,那么所有的区间会被强制转为指定的 closed 类型,因为 pd.IntervalIndex 只允许存放同一种开闭区间的 Interval 对象
my_interval
Interval(0, 1, closed='right')
my_interval_2
Interval(0.5, 1.5, closed='left')
pd.IntervalIndex([my_interval, my_interval_2], closed='left')
IntervalIndex([[0.0, 1.0), [0.5, 1.5)],
              closed='left',
              dtype='interval[float64]')

区间的属性与方法

​ 如果想要具体利用 cut 或者 qcut 的结果进行分析,那么需要先将其转为该种索引类型

id_interval = pd.IntervalIndex(pd.cut(s, 3))

IntervalIndex的常用属性:left, right, mid, length ,分别表示左右端点、两端点均值和区间长度

id_demo = id_interval[:5] # 选出前5个展示
id_demo
IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],
              closed='right',
              name='Weight',
              dtype='interval[float64]')

示例:

id_demo.left
Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')

containsoverlaps ,分别指逐个判断每个区间是否包含某元素,以及是否和一个 pd.Interval 对象有交集

id_demo.contains(4)
array([False, False, False, False, False])
id_demo.overlaps(pd.Interval(40,60))
array([ True,  True, False,  True, False])

时序数据

时间戳

Timestamp

​ 将日期格式转换成Timestamp类型:pd.Timestamp()

ts = pd.Timestamp('2020/1/1')
ts
Timestamp('2020-01-01 00:00:00')

​ Timestamp类型的属性:year, month, day, hour, min, second

​ Timestamp类型表示的时间范围最大是:585

pd.Timestamp.maxpd.Timestamp.min 可以获取时间戳表示的范围

Datetime序列

  • to_datetime

    ​ 能够把时间戳格式转化为datetime64[ns]类型的时间序列

pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])
DatetimeIndex(['2020-01-01', '2020-01-03', '2020-01-06'], dtype='datetime64[ns]', freq=None)

读取表便于学习

df = pd.read_csv('C:\Users\Yuan\Desktop\pandas\文件\data\learn_pandas.csv')
s = pd.to_datetime(df.Test_Date)
s.head()
0   2019-10-05
1   2019-09-04
2   2019-09-12
3   2020-01-03
4   2019-11-06
Name: Test_Date, dtype: datetime64[ns]

​ 时间戳格式如果不满足转换,可以使用**format****强制转换**进行匹配

temp = pd.to_datetime(['2020\1\1','2020\1\3'],format='%Y\%m\%d')
temp
DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)

列表传入返回DatetimeIndex对象

Series传入返回**datetime64[ns]**类型

​ 把表的多列时间属性拼接转为时间序列,但是列名必须与给定的属性关键词相同

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)
0   2020-01-01 10:30:20
1   2020-01-02 20:50:40
dtype: datetime64[ns]
  • date_range

    ​ 生成连续间隔时间的一种方法

    ​ 参数:``start, end, freq, periods`分别表示开始时间,结束时间,时间间隔,时间戳个数

    注意:开始或结束日期如果作为端点,如果计算到端点则它会被包含

pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含
DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')
pd.date_range('2020-1-1','2020-2-28', freq='10D')
DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31','2020-02-10', '2020-02-20'],
dtype='datetime64[ns]', freq='10D')
pd.date_range('2020-1-1','2020-2-28', periods=6) # 由于结束日期无法取到,freq不为10天
DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',
               '2020-01-24 04:48:00', '2020-02-04 19:12:00',
               '2020-02-16 09:36:00', '2020-02-28 00:00:00'],
              dtype='datetime64[ns]', freq=None)

这里的 freq 参数与 DateOffset 对象紧密相关

  • **asfreq**方法

    ​ 改变序列采样频率

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

dt对象

  • datetime64[ns].dt

datetime64[ns] 上的属性

​ 大致分为三类操作:取出时间相关的属性、判断时间戳是否满足条件、取整操作

取出时间相关的属性:``date, time, year, month, day, hour,minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter,**其中 daysinmonth, quarter` 分别表示该月一共有几天和季度。**

s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))
s.dt.daysinmonth
  • dayofweek

​ 返回周中的星期情况(周一为0、周二为1)

s.dt.dayofweek
0    2
1    3
2    4
dtype: int64

month_name, day_name 返回英文的月名和星期名,注意它们是方法而不是属性

s.dt.month_name()
0    January
1    January
2    January
dtype: object

判断时间戳是否满足条件

​ 测试是否为月/季/年的第一天或者最后一天

s.dt.is_year_start # 还可选 is_quarter/month_start
0     True
1    False
2    False
dtype: bool
s.dt.is_year_end # 还可选 is_quarter/month_end
0    False
1    False
2    False
dtype: bool

取整操作

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[(idx.is_month_start|idx.is_month_end).values].head()
2020-01-01    1
2020-01-31    0
2020-02-01    1
2020-02-29    1
2020-03-01    0
dtype: int32
  • 双休日
s[idx.dayofweek.isin([5,6]).values].head()
2020-01-04    1
2020-01-05    0
2020-01-11    0
2020-01-12    1
2020-01-18    1
dtype: int32
  • 取出单日值
s['2020-07'].head()
2020-07-01    0
2020-07-02    1
2020-07-03    0
2020-07-04    0
2020-07-05    0
Freq: D, dtype: int32
  • 取出5月初至7月15日
s['2020-05':'2020-7-15'].head()
2020-05-01    0
2020-05-02    1
2020-05-03    0
2020-05-04    1
2020-05-05    1
Freq: D, dtype: int32
s['2020-05':'2020-7-15'].tail()
2020-07-11    0
2020-07-12    0
2020-07-13    1
2020-07-14    0
2020-07-15    1
Freq: D, dtype: int32

时间差

Timedelta

pd.Timedelta是两个时间戳的

pd.Timedelta(days=1, minutes=25)# 需要注意加s
Timedelta('1 days 00:25:00')
pd.Timedelta('1 days 25 minutes') # 字符串生成
Timedelta('1 days 00:25:00')
  • pd.to_timedelta

    ​ 生成时间差序列,类型为 timedelta64[ns]

s = pd.to_timedelta(df.Time_Record)
s.head()
0   0 days 00:04:34
1   0 days 00:04:20
2   0 days 00:05:22
3   0 days 00:04:08
4   0 days 00:05:22
Name: Time_Record, dtype: timedelta64[ns]
  • pd.timedelta_range

    ​ 时间差序列,与``date_range`具有一致的参数

pd.timedelta_range('0s', '1000s', freq='6min')
TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')
pd.timedelta_range('0s', '1000s', periods=3)
TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)

####dt对象

Timedelta序列也定义了dt对象

​ 属性:days, seconds, mircroseconds, nanoseconds

这里的 seconds 不是指单纯的秒,而是对天数取余后剩余的秒数

s.dt.seconds.head()
0    274
1    260
2    322
3    248
4    322
Name: Time_Record, dtype: int64

​ 直接对应秒数:total_seconds

s.dt.total_seconds().head()
0    274.0
1    260.0
2    322.0
3    248.0
4    322.0
Name: Time_Record, dtype: float64

取整函数也是可以在 dt 对象上使用

pd.to_timedelta(df.Time_Record).dt.round('min').head()
0   0 days 00:05:00
1   0 days 00:04:00
2   0 days 00:05:00
3   0 days 00:04:00
4   0 days 00:05:00
Name: Time_Record, dtype: timedelta64[ns]

####Timedelta的运算

​ 三类:与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算

预设:

td1 = pd.Timedelta(days=1)
td2 = pd.Timedelta(days=3)
ts = pd.Timestamp('20200101')
ts - td1
Timestamp('2019-12-31 00:00:00')
td1 * 2
Timedelta('2 days 00:00:00')

时间差序列的运算

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

示例1:

td1 * 5
TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')

示例2:

td1 * pd.Series(list(range(5))) # 逐个相乘
0    0 days
1    2 days
2    6 days
3   12 days
4   20 days
dtype: timedelta64[ns]

示例3:

td1 + ts # 逐个相加
DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',
               '2020-01-10'],
              dtype='datetime64[ns]', freq=None)

日期偏置

Offset对象

​ **pd.offsets**定义offset对象

​ 日期偏置是一种和日历相关的特殊时间差

示例:

pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)
Timestamp('2020-09-07 00:00:00')

+ 获取最近的一个日期

- 获取最近的一个日期

pd.Timestamp('20200907') - pd.offsets.BDay(30)
Timestamp('2020-07-27 00:00:00')
pd.Timestamp('20200907') + pd.offsets.MonthEnd()
Timestamp('2020-09-30 00:00:00')
  • CDay

    ​ 特殊的offset对象

    ​ 参数:

    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
2020-01-08    2
2020-01-09    3
2020-01-10    4
2020-01-11    5
Freq: D, dtype: int64
[i + my_filter for i in dr]
[Timestamp('2020-01-10 00:00:00'),
 Timestamp('2020-01-10 00:00:00'),
 Timestamp('2020-01-15 00:00:00'),
 Timestamp('2020-01-15 00:00:00')]

不要使用部分 Offset:

​ 在当前版本下由于一些 bug不要使用 Day 级别以下的 Offset 对象,比如 Hour, Second 等,请使用对应的 Timedelta 对象来代替

偏置字符串

​ 偏执字符串就是Offset的缩写,作为类型一并给freq参数使用

示例:

pd.date_range('20200101','20200331', freq='MS') # 月初
DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')
pd.date_range('20200101','20200201',freq='WOM-1MON') # 每月第一个周一
DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')

上面的这些字符串,等价于使用的 Offset 对象

时序中的滑窗与分组

滑动窗口

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

​ **shift**函数可以指定freq单位进行滑动( datetime64 为索引的序列)

s.shift(freq='50D').head()
2020-02-20   -1
2020-02-21   -2
2020-02-22   -1
2020-02-25   -1
2020-02-26   -2
dtype: int32

diff

datetime64[ns] 的序列进行 diff 后就能够得到 timedelta64[ns] 的序列

my_series = pd.Series(s.index)
my_series.head()
0   2020-01-01
1   2020-01-02
2   2020-01-03
3   2020-01-06
4   2020-01-07
dtype: datetime64[ns]
 my_series.diff(1).head()
0      NaT
1   1 days
2   1 days
3   3 days
4   1 days
dtype: timedelta64[ns]

重采样

pd.resample

​ 与groupby用法类似,是对时间序列分组计算的分组对象

s.resample('10D').mean().head()
2020-01-01   -2.000000
2020-01-11   -3.166667
2020-01-21   -3.625000
2020-01-31   -4.000000
2020-02-10   -0.375000
Freq: 10D, dtype: float64

​ 重采样也可以使用**apply**

s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差
2020-01-01    3
2020-01-11    4
2020-01-21    4
2020-01-31    2
2020-02-10    4
Freq: 10D, dtype: int32

resample边界处理

​ 起始值的计算方法:从给定的最小时间戳的午夜0点开始,一直增加freq加到不超过这个时间戳,就被设置为起始值

​ 每次累加的freq参数作为分割节点进行分组,区间为左闭右开

idx = 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()
2020-01-01 08:26:35   -1
2020-01-01 08:27:52   -1
2020-01-01 08:29:09   -2
2020-01-01 08:30:26   -3
2020-01-01 08:31:43   -4
Freq: 77S, dtype: int32

下面对应的第一个组起始值为 08:24:00 ,其是从当天0点增加72个 freq=7 min 得到的,如果再增加一个 freq 则超出了序列的最小时间戳 08:26:35

s.resample('7min').mean().head()
2020-01-01 08:24:00   -1.750000
2020-01-01 08:31:00   -2.600000
2020-01-01 08:38:00   -2.166667
2020-01-01 08:45:00    0.200000
2020-01-01 08:52:00    2.833333
Freq: 7T, dtype: float64

​ 指定originstart:最小时间戳开始依次增加 freq 进行分组

s.resample('7min', origin='start').mean().head()
2020-01-01 08:26:35   -2.333333
2020-01-01 08:33:35   -2.400000
2020-01-01 08:40:35   -1.333333
2020-01-01 08:47:35    1.200000
2020-01-01 08:54:35    3.166667
Freq: 7T, dtype: float64
  • 返回值一般是组的第一个时间戳,但 M, A, Q, BM, BA, BQ, W 这七个是取对应区间的最后一个时间戳
s = pd.Series(np.random.randint(2,size=366),index=pd.date_range('2020-01-01','2020-12-31'))
s.resample('M').mean().head()
2020-01-31    0.451613
2020-02-29    0.448276
2020-03-31    0.516129
2020-04-30    0.566667
2020-05-31    0.451613
Freq: M, dtype: float64
s.resample('MS').mean().head() # 结果一样,但索引不同
2020-01-01    0.451613
2020-02-01    0.448276
2020-03-01    0.516129
2020-04-01    0.566667
2020-05-01    0.451613
Freq: MS, dtype: float64

归属人:我怕是有点打脑壳
严禁转载

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>