Pandas
文件读写
读取
pd.read_csv()
读取csvpd.read_excel()
读取excelpd.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 10020 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 inrange(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)
00.0000011.234562100.0000030.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是降序
以名字进行索引升序排序
序号 姓名 年龄 工资(元)
01 张茜 28290012 黄丽 32360023 李海涛 24280034 邓大海 26220045 曾丽萍 272500
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())deffunc(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
13.025.037.049.0511.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
21.033.046.0
dtype: float64
特殊用法(取负数)03.016.0210.0315.04 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
25.037.049.0
dtype: float64
特殊用法(取负数):0-5.01-7.02-9.03 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
25.00000032.33333341.500000
dtype: float64
特殊用法(取负数):0-0.8333331-0.7000002-0.6000003 NaN
4 NaN
dtype: float64
另类滑窗函数也可以用rolling函数来实现
扩展窗口
expanding
累计窗口函数,与滑动窗口不同的是他的窗口会动态增大,从第一个开始向右边平移n个元素进行计算
num5 = pd.Series([1,3,6,10,15])
num5.expanding().mean()
01.00000012.00000023.33333335.00000047.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()
SchoolGradeNameGenderWeightTransfer0Shanghai Jiao Tong UniversityFreshmanGaopeng YangFemale46.0N1Peking UniversityFreshmanChangqiang YouMale70.0N2Shanghai Jiao Tong UniversitySeniorMei SunMale89.0N3Fudan UniversitySophomoreXiaojuan SunFemale41.0N4Fudan UniversitySophomoreGaojuan YouMale74.0N
行和列
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
SchoolGenderNameQiang SunTsinghua UniversityFemaleQiang SunTsinghua UniversityFemaleQiang SunShanghai Jiao Tong UniversityFemaleQuan ZhaoShanghai Jiao Tong UniversityFemale
切片
Series 使用字符串索引时提到,如果是唯一值的起点和终点字符,那么就可以使用切片,并且包含两个端点,如果不唯一则报错
#两个端点都包含
df_demo.loc['Gaojuan You':'Gaoqiang Qian','School':'Gender']
SchoolGradeGenderNameGaojuan YouFudan UniversitySophomoreMaleXiaoli QianTsinghua UniversityFreshmanFemaleQiang ChuShanghai Jiao Tong UniversityFreshmanFemaleGaoqiang QianTsinghua UniversityJuniorFemale
需要注意的是,如果
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()
SchoolGradeGenderWeightTransferNameMei SunShanghai Jiao Tong UniversitySeniorMale89.0NGaojuan YouFudan UniversitySophomoreMale74.0NXiaopeng ZhouShanghai Jiao Tong UniversityFreshmanMale74.0NXiaofeng SunTsinghua UniversitySeniorMale71.0NQiang ZhengShanghai Jiao Tong UniversitySeniorMale87.0N
- 传入元素列表,也可以通过
isin
方法返回的布尔列表等价写出
# 选出所有大一和大四的同学信息
df_demo.loc[df_demo.Grade.isin(['Freshman','Senior'])].head()#isin是dataframe的一个方法
SchoolGradeGenderWeightTransferNameGaopeng YangShanghai Jiao Tong UniversityFreshmanFemale46.0NChangqiang YouPeking UniversityFreshmanMale70.0NMei SunShanghai Jiao Tong UniversitySeniorMale89.0NXiaoli QianTsinghua UniversityFreshmanFemale51.0NQiang ChuShanghai Jiao Tong UniversityFreshmanFemale52.0N
- 对于复合条件而言,可以用 |(或), &(且), ~(取反) 的组合来实现
#选出复旦大学中体重超过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]
SchoolGradeGenderWeightTransferNameQiang HanPeking UniversityFreshmanMale87.0NChengpeng ZhouFudan UniversitySeniorMale81.0NChangpeng ZhaoPeking UniversityFreshmanMale83.0NChengpeng QianFudan UniversitySeniorMale73.0Y
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()
SchoolGradeGenderWeightTransferNameGaopeng YangShanghai Jiao Tong UniversityFreshmanFemale46.0NChangqiang YouPeking UniversityFreshmanMale70.0NMei SunShanghai Jiao Tong UniversitySeniorMale89.0NXiaojuan SunFudan UniversitySophomoreFemale41.0NGaojuan YouFudan UniversitySophomoreMale74.0N
字符串dtypes是不允许的,使用’object’代替
函数
必须以前面的四种合法形式之一为返回值,并且函数的输入值为DataFrame本身(支持使用 lambda 表达式)
defcondition(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]
SchoolGradeGenderWeightTransferNameQiang HanPeking UniversityFreshmanMale87.0NChengpeng ZhouFudan UniversitySeniorMale81.0NChangpeng ZhaoPeking UniversityFreshmanMale83.0NChengpeng QianFudan UniversitySeniorMale73.0Y
slice()
返回切片对象,主要作用在切片操作函数里的参数传递,由于函数无法返回(如 start: end: step )的切片形式,故返回切片时要用slice
对象进行包装
df_demo.loc[lambda x:slice('Gaojuan You','Gaoqiang Qian')]#start:end:step
SchoolGradeGenderWeightTransferNameGaojuan YouFudan UniversitySophomoreMale74.0NXiaoli QianTsinghua UniversityFreshmanFemale51.0NQiang ChuShanghai Jiao Tong UniversityFreshmanFemale52.0NGaoqiang QianTsinghua UniversityJuniorFemale50.0N
对于 Series 也可以使用 loc 索引,其遵循的原则与 DataFrame 中用于行筛选的 loc[*] 完全一致
SettingWithCopyWarning
在对表或者序列赋值时,应当在使用一层索引器后直接进行赋值操作,这样做是由于进行多次索引后赋值是赋在临时返回的 copy 副本上的,而没有真正修改元素从而报出
SettingWithCopyWarning
警告。
df_chain[df_chain.A!=0].B =1'''
这样使用会出问题的原因就是索引后数据在内存中
再去直接调用属性并赋值pandas就会报出警告并且'不执行'这个语句
但是可以使用索引器来达到这个目的
'''
C:\Python310\lib\site-packages\pandas\core\generic.py:5516: SettingWithCopyWarning:
A value is trying to be set on a copy of a slicefrom 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
0001112-11
iloc索引器
针对位置进行筛选/查找,在相应的 * 位置处一共也有五类合法对象,分别是:整数、整数列表、整数切片、布尔列表以及函数,函数的返回值必须是前面的四类合法对象中的一个,其输入同样也为
DataFrame
本身
df_demo.iloc[1,1]# 第二行第二列
'Freshman'
df_demo.iloc[[0,1],[0,1]]# 前两行前两列
SchoolGradeNameGaopeng YangShanghai Jiao Tong UniversityFreshmanChangqiang YouPeking UniversityFreshman
- 切片不包含结束端点
df_demo.iloc[1:4,2:4]# 切片不包含结束端点
GenderWeightNameChangqiang YouMale70.0Mei SunMale89.0Xiaojuan SunFemale41.0
- 传入切片为返回值的函数
df_demo.iloc[lambda x:slice(1,4)]
SchoolGradeGenderWeightTransferNameChangqiang YouPeking UniversityFreshmanMale70.0NMei SunShanghai Jiao Tong UniversitySeniorMale89.0NXiaojuan SunFudan UniversitySophomoreFemale41.0N
- 布尔列表 在使用布尔列表的时候要特别注意,不能传入
Series
而必须传入序列的values
,否则会报错。因此,在使用布尔筛选的时候还是应当优先考虑 loc 的方式
df_demo.iloc[(df_demo.Weight>80).values].head()# .values 将布尔的每个值传入索引器
SchoolGradeGenderWeightTransferNameMei SunShanghai Jiao Tong UniversitySeniorMale89.0NQiang ZhengShanghai Jiao Tong UniversitySeniorMale87.0NQiang HanPeking UniversityFreshmanMale87.0NChengpeng ZhouFudan UniversitySeniorMale81.0NFeng HanShanghai Jiao Tong UniversitySophomoreMale82.0N
对
Series
而言同样也可以通过 iloc 返回相应位置的值或子序列
query方法
pandas可以支持字符串形式传入 **
query
** 方法来查询数据,表达式执行结果必须返回布尔列表
优点:使用query方法可以无需重复使用
DataFrame
的列名来索引,但一般使用的代码长度尽量较少(在不降低可读性的前提下)
df.query('((School == "Fudan University")&'' (Grade == "Senior")&'' (Weight > 70))|''((School == "Peking University")&'' (Grade != "Senior")&'' (Weight > 80))')
SchoolGradeNameGenderWeightTransfer38Peking UniversityFreshmanQiang HanMale87.0N66Fudan UniversitySeniorChengpeng ZhouMale81.0N99Peking UniversityFreshmanChangpeng ZhaoMale83.0N131Fudan UniversitySeniorChengpeng QianMale73.0Y
在 query 表达式中,自动帮用户注册了所有来自
DataFrame
的列名,所有属于该
Series
的方法都可以被调用,和正常的函数调用并没有区别
对于含有空格的列名,需要使用 ‘
col name
’ 的(加引号)方式进行引用。
- 在 query 中还注册了若干英语的字面用法,帮助提高可读性,例如:
or, and, or, is in, not in
df.query('(Grade notin["Freshman","Sophomore"])and(Gender =="Male")').head()
SchoolGradeNameGenderWeightTransfer2Shanghai Jiao Tong UniversitySeniorMei SunMale89.0N16Tsinghua UniversityJuniorXiaoqiang QinMale68.0N17Tsinghua UniversityJuniorPeng WangMale65.0N18Tsinghua UniversitySeniorXiaofeng SunMale71.0N21Shanghai Jiao Tong UniversitySeniorXiaopeng ShenMale62.0NaN
- 在字符串中出现与列表的比较时,
==
和!=
分别表示元素出现在列表或没有出现在列表,等价于is in
和not in
df.query('Grade == ["Junior", "Senior"]').head()
SchoolGradeNameGenderWeightTransfer2Shanghai Jiao Tong UniversitySeniorMei SunMale89.0N7Tsinghua UniversityJuniorGaoqiang QianFemale50.0N9Peking UniversityJuniorJuan XuFemaleNaNN11Tsinghua UniversityJuniorXiaoquan LvFemale43.0N12Shanghai Jiao Tong UniversitySeniorPeng YouFemale48.0NaN
- 对于 query 中的字符串,如果要引用外部变量,只需在变量名前加
@
符号
low, high =70,80
df.query('Weight.between(@low, @high)').head()
SchoolGradeNameGenderWeightTransfer1Peking UniversityFreshmanChangqiang YouMale70.0N4Fudan UniversitySophomoreGaojuan YouMale74.0N10Shanghai Jiao Tong UniversityFreshmanXiaopeng ZhouMale74.0N18Tsinghua UniversitySeniorXiaofeng SunMale71.0N35Peking UniversityFreshmanGaoli ZhaoMale78.0N
随机抽样
想要对样本或特征进随机抽样就可以用
sample
函数
sample(n , axis , frac , replace , weights)
n:抽样数量
axis:抽样方向(0为行,1为列)
frac:抽样比例(1为100%)
replace:是否放回(True/False)
weights:每个样本抽样的相对概率
df_sample = pd.DataFrame({'id':list('abcde'),'value':[1,2,3,4,90]})
df_sample
idvalue0a11b22c33d44e90
示例:
df_sample.sample(3, replace =True, weights = df_sample.value)'''
抽样数量为3,有放回,"weights是把vaolue整列的记录数除以整列的和"
'''
idvalue4e904e904e90
多级索引
多级索引及其表的结构
- 创建表便于学习
#先创建一个表,用来示例,之后会讲到
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.8165.0167.9174.260.655.163.365.8
Male 172.3158.1167.8162.271.271.063.163.5
B Female 162.5165.1163.7170.359.857.956.574.8
Male 166.8163.6165.2164.762.562.858.768.9
C Female 170.5162.0164.6158.756.963.960.566.9
Male 150.2166.3167.3159.362.459.164.967.1
D Female 174.3155.7163.2162.165.366.561.863.2
Male 170.7170.3163.8164.961.663.260.956.4
多级索引与单层索引的表一样,具备元素值,行索引,列索引三个部分,行索引和列索引都是**
MultiIndex
类型,只不过索引中的元素是元组型** 而不是单层索引中的标量
- 索引的 名字和值属性 分别可以通过
names
和values
获得
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()
NameGenderWeightTransferSchoolGradeShanghai Jiao Tong UniversityFreshmanGaopeng YangFemale46.0NPeking UniversityFreshmanChangqiang YouMale70.0NShanghai Jiao Tong UniversitySeniorMei SunMale89.0NFudan UniversitySophomoreXiaojuan SunFemale41.0NSophomoreGaojuan YouMale74.0N
- 由于多级索引中的单个元素以元组为单位,之前介绍的
loc
和iloc
方法可以完全照搬,只需把标量的位置替换成对应的元组
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()
NameGenderHeightWeightTransferTest_NumberTest_DateTime_RecordSchoolGradeShanghai Jiao Tong UniversityFreshmanGaopeng YangFemale158.946.0N12019/10/50:04:34Peking UniversityFreshmanChangqiang YouMale166.570.0N12019/9/40:04:20Shanghai Jiao Tong UniversitySeniorMei SunMale188.989.0N22019/9/120:05:22Fudan UniversitySophomoreXiaojuan SunFemaleNaN41.0N22020/1/30:04:08Tsinghua UniversityFreshmanXiaoli QianFemale158.051.0N12019/10/310:03:47
df_unique.sort_index().loc[('Fudan University','Senior'):].head()'''
sort_index()先以索引进行排序
loc[('Fudan University', 'Senior'):] 定位('Fudan University', 'Senior')这一行后输出这一行和后面所有内容
'''
NameGenderWeightTransferSchoolGradeFudan UniversitySeniorChengpeng ZhengFemale38.0NSophomoreXiaojuan SunFemale41.0NPeking UniversityFreshmanChangqiang YouMale70.0NJuniorJuan XuFemaleNaNNSeniorChangli LvFemale41.0N
- 可对多层的元素进行交叉组合后索引, 但同时需要指定 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 36-9-6-6-209-5
b -33-8-3-258-44
c -107-466-99-6
B a 85-2-9-80-91-6
b 29-7-9-9-5-4-3-1
c 86-501-8-8-20
C a -6-3259-95-63
b 12-5-3-56-63-5
c -156-66478-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 259-95-63
b -5-3-56-63-5
c 6-66478-4
另外,也支持布尔序列的索引
df_ex.loc[idx[:'A',lambda x:x.sum()>0]]# 列和大于0
Big D F
Small d e e
Upper Lower
A a 369
b -33-4
c -109
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 -25-44
c 669-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 36-9-6-6-209
beta -5-33-8-3-258
b alpha -44-107-466
beta -99-685-2-9-8
B a alpha 0-91-629-7-9
beta -9-5-4-3-186-5
b alpha 01-8-8-20-6-3
beta 259-95-631
索引层交换由
swaplevel
和
reorder_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 36-9-6-6-209
beta -5-33-8-3-258
b alpha -44-107-466
beta -99-685-2-9-8
B a alpha 0-91-629-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 36-9-6-6-209
beta A a -5-33-8-3-258
alpha A b -44-107-466
beta A b -99-685-2-9-8
alpha B a 0-91-629-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 36-9-6-6-209
beta -5-33-8-3-258
b alpha -44-107-466
beta -99-685-2-9-8
B a alpha 0-91-629-7-9
beta -9-5-4-3-186-5
b alpha 01-8-8-20-6-3
beta 259-95-631
属性的修改
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 36-9-6-6-209
beta -5-33-8-3-258
b alpha -44-107-466
beta -99-685-2-9-8
B a alpha 0-91-629-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 36-9-6-6-209
beta -5-33-8-3-258
b alpha -44-107-466
beta -99-685-2-9-8
B a alpha 0-91-629-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 36-9-6-6-209
BETA -5-33-8-3-258
b ALPHA -44-107-466
BETA -99-685-2-9-8
B a ALPHA 0-91-629-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 36-9-6-6-209
b -5-33-8-3-258
b c -44-107-466
d -99-685-2-9-8
B a e 0-91-629-7-9
f -9-5-4-3-186-5
b g 01-8-8-20-6-3
h 259-95-631
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 36-9-6-6-209
BETA -5-33-8-3-258
b ALPHA -44-107-466
BETA -99-685-2-9-8
B a ALPHA 0-91-629-7-9
设置与重置
#构建新表便于学习
df_new = pd.DataFrame({'A':list('aacd'),'B':list('PQRT'),'C':[1,2,3,4]})
df_new
ABC0aP11aQ22cR33dT4
set_index
设置索引 主要参数是append
,表示是否保留原来的索引,直接把设定的索引添加到原来的索引内层
df_new.set_index('A')#直接把A列设置为索引层
BCAaP1aQ2cR3dT4
df_new.set_index('A', append=True)#列索引A被添加到内层
BCA0aP11aQ22cR33dT4
如果 想要添加索引的列 没有出现在其中,那么可以直接在参数中传入相应的 Series
my_index = pd.Series(list('WXYZ'), name='D')
df_new = df_new.set_index(['A', my_index])#my_index 插入了列名为D的列
df_new
BCADaWP1XQ2cYR3dZT4
reset_index
重设索引 主要参数是drop
,表示是否要把去掉的索引层丢弃,而不是添加到列中
df_new.reset_index(['D'])#没有丢弃去掉的索引,而是从第二层返回到了第一层
DBCAaWP1aXQ2cYR3dZT4
df_new.reset_index(['D'], drop=True)#直接丢弃去掉的索引
BCAaP1aQ2cR3dT4
如果 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
原表
WeightHeight100160176100370180100280179
print('修改后')
df_reindex.reindex(index=['1001','1002','1003','1004'],columns=['Weight','Gender'])
修改后
WeightGender100160.0NaN100280.0NaN100370.0NaN1004NaNNaN
reindex_like
df_existed = pd.DataFrame(index=['1001','1002','1003','1004'],columns=['Weight','Gender'])print('原表')
df_existed
原表
WeightGender1001NaNNaN1002NaNNaN1003NaNNaN1004NaNNaN
print('修改后')
df_reindex.reindex_like(df_existed)
修改后
WeightGender100160.0NaN100280.0NaN100370.0NaN1004NaNNaN
运算
经常会有一种利用集合运算来取出符合条件行的需求,对索引可以使用集合的运算
由于集合的元素是互异的,但是索引中可能有相同的元素,先用 **
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()
SchoolGradeNameGenderHeightWeightTransferTest_NumberTest_DateTime_Record0Shanghai Jiao Tong UniversityFreshmanGaopeng YangFemale158.946.0N12019/10/50:04:341Peking UniversityFreshmanChangqiang YouMale166.570.0N12019/9/40:04:202Shanghai Jiao Tong UniversitySeniorMei SunMale188.989.0N22019/9/120:05:223Fudan UniversitySophomoreXiaojuan SunFemaleNaN41.0N22020/1/30:04:084Fudan UniversitySophomoreGaojuan YouMale174.074.0N
#按照性别统计身高中位数
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
False159.034646True172.705357
Name: Height, dtype: float64
- 由此可以看出是以分组依据的
unique
值来分组,传入的序列都会被去重
# 通过 drop_duplicates 就能知道具体的组类别
df[['School','Gender']].drop_duplicates()
SchoolGender0Shanghai Jiao Tong UniversityFemale1Peking UniversityMale2Shanghai Jiao Tong UniversityMale3Fudan UniversityFemale4Fudan UniversityMale5Tsinghua UniversityFemale9Peking UniversityFemale16Tsinghua UniversityMale
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'))
SchoolGradeNameGenderHeightWeightTransferTest_NumberTest_DateTime_Record1Peking UniversityFreshmanChangqiang YouMale166.570.0N12019/9/40:04:2032Peking UniversityFreshmanGaopeng ShiFemale162.948.0N12019/9/120:04:5835Peking UniversityFreshmanGaoli ZhaoMale175.478.0N22019/10/80:03:3236Peking UniversityFreshmanXiaojuan QinMaleNaN79.0Y12019/12/100:04:1038Peking UniversityFreshmanQiang HanMale185.387.0N32020/1/70:03:5845Peking UniversityFreshmanQuan ChuFemale154.743.0N12019/11/280:04:4754Peking UniversityFreshmanXiaojuan ChuMale162.458.0Y32019/11/290:03:4257Peking UniversityFreshmanChangquan ChuFemale159.645.0N22019/12/90:04:1888Peking UniversityFreshmanXiaopeng HanFemale164.153.0N12019/12/180:05:2096Peking UniversityFreshmanChangmei FengFemale163.856.0N32019/11/80:04:4199Peking UniversityFreshmanChangpeng ZhaoMale181.383.0N22019/10/240:04:08140Peking UniversityFreshmanQiang ZhangFemale152.743.0N12019/11/300:05:27185Peking UniversityFreshmanChunmei WangFemale151.243.0N22019/12/100: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.263.0
Male 193.989.0
groupby对象必须聚合后才会输出DataFrame
agg方法
传入函数并运行的是数据源
- 使用多个函数 当使用多个聚合函数时,需要用列表的形式把内置聚合函数对应的字符串传入,先前提到的所有字符串都是合法的
gb.agg(['sum','idxmax','skew'])
Height Weight
sum idxmax skew sum idxmax skew
Gender
Female 21014.028-0.2192536469.028-0.268482
Male 8854.91930.4375353929.02-0.332393
从结果看,此时列索引为多级索引,第一层为数据源,第二层为使用的聚合方法,分别逐一队列使用聚合,因此结果为6列
- 对特定的列使用特定的聚合函数 通过构造字典传入 agg 中实现 列名为键,聚合字符串或字符串列表为值
gb.agg({'Height':['mean','max'],'Weight':'count'})#列名为键,聚合字符串为值,但是这里只是数据来源,不是分组依据,还是对性别进行分组
Height Weight
mean max count
Gender
Female 159.19697170.2135
Male 173.62549193.954
- 自定义函数 必须注意传入函数的参数是之前数据源中的列,逐列进行计算
gb.agg(lambda x: x.mean()-x.min())
Height Weight
Gender
Female 13.7969713.918519
Male 17.9254921.759259
由于传入的是列,因此列上的方法和属性都是可以在函数中使用的,只需保证返回值是标量即可
defmy_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.821014.029.06469.0
Male 38.28854.938.03929.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.063.0
Male High 8854.989.0
- 使用对一个或者多个列使用单个聚合的时,重命名需要加方括号
gb.agg([('my_sum','sum')])#必须加中括号
Height Weight
my_sum my_sum
Gender
Female 21014.06469.0
Male 8854.93929.0
gb.agg({'Height':[('my_func', my_func),'sum'],'Weight':[('range',lambda x:x.max())]})
Height Weight
my_func sumrange
Gender
Female Low 21014.063.0
Male High 8854.989.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
0158.946.01166.570.02188.989.03 NaN 46.04188.989.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.3548881-1.010925-0.35500022.1670632.0894983 NaN -1.27978940.0531330.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
0159.1969747.9185191173.6254972.7592592173.6254972.7592593159.1969747.9185194173.6254972.759259'''
数据来源是['Height', 'Weight'],但是结果没有给出分组依据,而是把聚合结果直接填充到Gender对应的位置上(Female,Male对应的行填充了对应的值)
'''
agg:
agg = gb.agg('mean')
Height Weight
Gender
Female 159.1969747.918519
Male 173.6254972.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指标defBMI(x):
Height = x['Height']/100
Weight = x['Weight']
BMI_value = Weight/Height**2return 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 102030
Male 102030
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 100200300
Male 100200300
- 返回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.01.0
b 1.01.02 a 1.01.0
b 1.01.03 a 1.01.0
b 1.01.0
Male 1 a 1.01.0
b 1.01.02 a 1.01.0
b 1.01.03 a 1.01.0
b 1.01.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 1631 F 1602 M 1753 M 180
Height: F Height: M
01631751160180
显然这两张表从信息上是完全等价的,它们包含相同的身高统计数值,只是这些数值的呈现方式不同,而其呈现方式主要又与性别一列选择的布局模式有关,即到底是以 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
01 San Zhang Chinese 8011 San Zhang Math 7522 Si Li Chinese 9032 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 8075
Si Li 9085
由于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
01 San Zhang Mid Chinese 801011 San Zhang Final Chinese 751522 Si Li Mid Chinese 852132 Si Li Final Chinese 651541 San Zhang Mid Math 902051 San Zhang Final Math 85762 Si Li Mid Math 92672 Si Li Final Math 882
示例:
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 Name1 San Zhang 8075908510152072 Si Li 85659288211562
根据唯一性原则,新表的**行索引等价于对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 801 San Zhang Chinese 902 San Zhang Math 1003 San Zhang Math 904 Si Li Chinese 705 Si Li Chinese 806 Si Li Math 857 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 8595
Si Li 7590
这里
aggfunc
传入的参数包含所有的聚合字符串,此外还可以传入以序列为输入 标量为输出的聚合函数来实现自定义操作
df.pivot_table(index ='Name',
columns ='Subject',
values ='Grade',
aggfunc =lambda x:x.mean())
Subject Chinese Math
Name
San Zhang 8595
Si Li 7590
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 8595.090.00
Si Li 7590.082.50
All 8092.586.25
宽表变长表
melt
宽表转长表
创建表以便学习
df = pd.DataFrame({'Class':[1,2],'Name':['San Zhang','Si Li'],'Chinese':[80,90],'Math':[80,75]})
df
Class Name Chinese Math
01 San Zhang 808012 Si Li 9075
参数:
'''
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
01 San Zhang Chinese 8012 Si Li Chinese 9021 San Zhang Math 8032 Si Li Math 75
**
melt
和
pivot
是一组互逆过程**,那么就一定可以通过pivot操作把
df_melted转回df的形式
df_unmelted = df_melted.pivot(index =['Class','Name'],
columns='Subject',
values='Grade')
df_unmelted # 下面需要恢复索引,并且重命名列索引名称
Subject Chinese Math
Class Name1 San Zhang 80802 Si Li 9075
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
01 San Zhang 8090809012 Si Li 75857585
参数:
'''
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 8090
Final 80902 Si Li Mid 7585
Final 7585
示例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 8010
Chinese_Final 7515
Math_Mid 9020
Math_Final 8572 Si Li Chinese_Mid 8521
Chinese_Final 6515
Math_Mid 926
Math_Final 882
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
01 San Zhang Mid Chinese 801011 San Zhang Final Chinese 751522 Si Li Mid Chinese 852132 Si Li Final Chinese 651541 San Zhang Mid Math 902051 San Zhang Final Math 85762 Si Li Mid Math 92672 Si Li Final Math 882
索引的变形
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
'''
之前提到了利用
swaplevel
或
reorder_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.01.0
dog small 1.01.0
B cat big 1.01.0
dog small 1.01.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.01.0
big 1.01.0
B cat big 1.01.0
dog small 1.01.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.01.01.01.0
index_2 1.01.01.01.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.946.0 N 12019/10/50:04:341 Peking University Freshman Changqiang You Male 166.570.0 N 12019/9/40:04:202 Shanghai Jiao Tong University Senior Mei Sun Male 188.989.0 N 22019/9/120:05:223 Fudan University Sophomore Xiaojuan Sun Female NaN 41.0 N 22020/1/30:04:084 Fudan University Sophomore Gaojuan You Male 174.074.0 N 22019/11/60: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 381
Peking University 282
Shanghai Jiao Tong University 530
Tsinghua University 624
如果给出了
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.01.0
Peking University 28.02.0
Shanghai Jiao Tong University 53.0 NaN
Tsinghua University 62.04.0
可以使用
pivot_table
进行等价操作,由于这里统计的是组合的频数,所以
values
参数无论传入那一个列都不会影响最后的结果
df.pivot_table(index ='School',
columns ='Transfer',
values ='Name',
aggfunc ='count')
Transfer N Y
School
Fudan University 38.01.0
Peking University 28.02.0
Shanghai Jiao Tong University 53.0 NaN
Tsinghua University 62.04.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]11 my_str 12{1,2}130314 dtype: int64 1
示例1:
df_ex.explode('A')#以A列把所有的拆分
A B
0110211 my_str 1211221331341
get_dummies
把类别特征转化为指示变量
#对年级一列转为指示变量,属于某一个年级的对应列标记为1,否则为0:
pd.get_dummies(df.Grade).head()
Freshman Junior Senior Sophomore
0100011000200103000140001
##连接
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_on
和
right_on
df1 = pd.DataFrame({'df1_name':['San Zhang','Si Li'],'Age':[20,30]})
df2 = pd.DataFrame({'df2_name':['Si Li','Wu Wang'],'Gender':['F','M']})
df1.merge(df2,
left_on='df1_name',
right_on='df2_name',
how='left')
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 7080
如果键不是唯一的,那么结果就会产生问题。举例中的行数很少,但如果实际数据中有几十万到上百万行的进行合并时,如果想要保证唯一性,除了用 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
分别表示拼接方向,连接形式,以及在新表中指示来自于哪一张旧表的名字意,
join
和
keys
与之前提到的 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 201 Si Li 300 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 2080 M
1 Si Li 3090 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.080.02 NaN NaN 90.0
pd.concat([df1, df2], axis=1, join='inner')
Name Age Grade
1 Si Li 3080
当确认要使用多表连接的方向合并时,尤其是横向的合并,可以先用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 201 Si Li 21
two 0 Wu Wang 21
序列与表的合并
如果想把序列追加到表的行末或者列末,可以分别使用
append
和
assign
方法
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 201 Si Li 212 Wu Wang 21
assign
一般通过df['new_col']=...
追加新列,缺点是会直接改动原表,所以assign返回的是一个临时副本
s = pd.Series([80,90])
df1.assign(Grade=s)#追加列
Name Age Grade
0 San Zhang 20801 Si Li 2190
df1['Grade']= s
df1
Name Age Grade
0 San Zhang 20801 Si Li 2190
类连接操作
比较
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
,其中
other
和
self
分别代指传入的参数表和被调用的表自身
如果想要完整显示表中的所有元素的比较情况,可以设置
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
能够让两张表按照一定规则进行组合,在进行规则比较的时候列索引会自动对其
defchoose_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.06.0 NaN
2 NaN NaN NaN NaN
DataFrame.mask() 替换条件为 True 的值
overtwrite
参数为
False
可以保留被调用表中未出现在传入的参数表中的列,而不会设置为缺失值
df1.combine(df2, choose_min, overwrite=False)#A列是没有进行比较的
A B C D
01.0 NaN NaN NaN
12.04.06.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
013.0127.0268.0
缺失值
###统计
isna
或isnull
查看每个单元格是否缺失
读取表以便学习
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
0FalseFalseFalseFalseFalseFalse1FalseFalseFalseFalseFalseFalse2FalseFalseFalseFalseFalseFalse3FalseFalseFalseTrueFalseFalse4FalseFalseFalseFalseFalseFalse
如果想要查看某一列缺失或者非缺失的行,可以利用 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,notna
和
any,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.062.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.946.0 N
1 Freshman Changqiang You Male 166.570.0 N
2 Senior Mei Sun Male 188.989.0 N
4 Sophomore Gaojuan You Male 174.074.0 N
5 Freshman Xiaoli Qian Female 158.051.0 N
删除
dropna
主要参数为方向**axis
(默认为0,即删除行),删除方式how
,删除的非缺失值个数阈值thresh
(非缺失值 没有达到这个数量的相应维度会被删除),备选的删除子集subset
,其中how
**主要有any
和all
两种参数可以选择
参数:
'''
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()
0158.9000001166.5000002188.9000003163.0758624174.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
TrueFalse
在 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
0True1False
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>11
dtype: Int64
示例2:
pd.Series([np.nan,True], dtype ='boolean')
0<NA>1True
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
0True1<NA>
dtype: boolean
s_boolean |True
0True1True
dtype: boolean
~s_boolean # 取反操作同样是无法唯一地判断缺失结果
0False1<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()
02.015.02 NaN
39.0414.0
dtype: float64
除了
np.nan**0
或
np.nan**1
这两种情况为确定值之外(结果为
1.0
),所有运算返回结果全为缺失(pd.NA的行为与此一致),并且
np.nan
在**比较操作时一定返回
False
,而
pd.NA
返回
pd.NA
**
diff与pct_change
前者凡是缺失计算都设置为缺失值,后者**缺失位置会被设置为
0%
的变化率**
diff
s.diff()
0 NaN
11.02 NaN
3 NaN
41.0
dtype: float64
pct_change()
s.pct_change()
0 NaN
10.50000020.00000030.33333340.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 11 a 32 b 53 NaN 74 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
01001100201030014001
文本数据
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是否展开多个列
0120 上海 黄浦 方浜中路249号
1 上海 宝山 密山路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('\s\wat')#给出正则模式,查找包含了这个正则的字符串元素,如果找得到就返回True
0True1True2False
dtype:bool
str.startswith()
返回以给定模式开始的布尔序列
str.endwith()
返回以给定模式结束的布尔序列
他们都不支持正则表达式
s.str.startswith('my')#指定开始字符
0True1False2False
dtype:bool
s.str.endswith('t')#指定结束字符
0True1True2False
dtype:bool
str.match()
返回每组字符串是否符合正则表达式的布尔序列
s.str.match('m|h')
0True1True2False
dtype:bool
s.str[::-1].str.match('ta[f|g]|n')# 反转后匹配
0False1True2True
dtype:bool
当然,这些也能通过在 str.contains 的正则中使用 ^ 和
$
来实现
s.str.contains('^[m|h]')
0True1True2False
dtype:bool
返回索引的匹配函数
**
str.find()
** 返回从左到右第一次匹配的位置索引,未找到则返回
-1
**
str.rfind()
** 返回从右到左第一次匹配的位置索引,未找到则返回
-1
s = pd.Series(['This is an apple. That is not an apple.'])
s.str.find('apple')
011
dtype: int64
s.str.rfind('apple')
033
dtype: int64
替换
str.replace()
str.replace
和
replace
并不是一个函数,在使用字符串替换时应当使用前者
参数:
'''
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'}
创建字符串替换函数:
#创建字符替换函数defmy_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.2491 Shanghai BS District Mishan Road No.52 Beijing CP District Beinong Road No.2
dtype:object
- 可以使用 命名子组 更加清晰地写出子组代表的含义
#创建正则表达式并给每个组命名
pat ='(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'#<>中间的就是组的名字defmy_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.2491 Shanghai BS District Mishan Road No.52 Beijing CP District Beinong Road No.2
dtype:object
提取
str.extract(regex)
用正则表达式提取字符串(只匹配一个)
pat ='(\w+市)(\w+区)(\w+路)(\d+号)'
s.str.extract(pat)
01230 上海市 黄浦区 方浜中路 249号
1 上海市 宝山区 密山路 5号
2 北京市 昌平区 北农路 2号
- 通过子组的命名,可以直接对新生成 DataFrame 的列命名
pat ='(?P<市名>\w+市)(?P<区名>\w+区)(?P<路名>\w+路)(?P<编号>\d+号)'
s.str.extract(pat)
市名 区名 路名 编号
0 上海市 黄浦区 方浜中路 249号
1 上海市 宝山区 密山路 5号
2 北京市 昌平区 北农路 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)
01
match
my_A 0135151265
my_B 067421256
pat_with_name ='[A|B](?P<name1>\d+)[T|S](?P<name2>\d+)'
s.str.extractall(pat_with_name)
01
match
my_A 0135151265
my_B 067421256
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
对字符格式的数值进行快速转换和筛选,其主要参数包括
errors
和
downcast
分别代表了非数的处理模式和转换类型。其中,对于不能转换为数值的有三种
errors
选项,
raise
,
coerce
,
ignore
分别表示直接报错、设为缺失以及保持原来的字符串
s = pd.Series(['1','2.2','2e','??','-2.1','0'])
pd.to_numeric(s, errors='ignore')
0112.222e3 ??
4-2.150
dtype:object
pd.to_numeric(s, errors='coerce')
01.012.22 NaN
3 NaN
4-2.150.0
dtype: float64
- 在数据清洗的时候,可以利用**
coerce
快速查看非数值型的行**
s[pd.to_numeric(s, errors='coerce').isna()]
22e3 ??
dtype:object
统计型函数
count
和
len
的作用分别是返回出现正则表达式匹配的次数和字符串的长度
s = pd.Series(['cat rat fat at','get feed sheet heat'])
s.str.count('[r|f]at|ee')
0212
dtype: int64
s.str.len()
014119
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,both
,
both
是两边一起填充
- 填充型的pad()可以用
rjust,ljust,center
来等效完成
**
ljust
是右填充,
rjust
是左填充**
- 在读取 excel 文件时,经常会出现数字前补0的需求除了可以使用上面的左侧填充函数进行操作之外,还可用
zfill
来实现
例如证券代码读入的时候会把”000007”作为数值7来处理
s = pd.Series([7,155,303000]).astype('string')
s.str.zfill(6)
000000710001552303000
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()
0010223343
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_index
** 和 **
sort_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
参数改成
q
,
q
参数是指
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')
contains
和
overlaps
,分别指逐个判断每个区间是否包含某元素,以及是否和一个
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.max
和
pd.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()
02019-10-0512019-09-0422019-09-1232020-01-0342019-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)
02020-01-0110:30:2012020-01-0220: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
021324
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
0True1False2False
dtype:bool
s.dt.is_year_end # 还可选 is_quarter/month_end
0False1False2False
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-0112020-01-3102020-02-0112020-02-2912020-03-010
dtype: int32
- 双休日
s[idx.dayofweek.isin([5,6]).values].head()
2020-01-0412020-01-0502020-01-1102020-01-1212020-01-181
dtype: int32
- 取出单日值
s['2020-07'].head()
2020-07-0102020-07-0212020-07-0302020-07-0402020-07-050
Freq: D, dtype: int32
- 取出5月初至7月15日
s['2020-05':'2020-7-15'].head()
2020-05-0102020-05-0212020-05-0302020-05-0412020-05-051
Freq: D, dtype: int32
s['2020-05':'2020-7-15'].tail()
2020-07-1102020-07-1202020-07-1312020-07-1402020-07-151
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()
00 days 00:04:3410 days 00:04:2020 days 00:05:2230 days 00:04:0840 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()
02741260232232484322
Name: Time_Record, dtype: int64
直接对应秒数:
total_seconds
s.dt.total_seconds().head()
0274.01260.02322.03248.04322.0
Name: Time_Record, dtype: float64
取整函数也是可以在
dt
对象上使用
pd.to_timedelta(df.Time_Record).dt.round('min').head()
00 days 00:05:0010 days 00:04:0020 days 00:05:0030 days 00:04:0040 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)))# 逐个相乘
00 days
12 days
26 days
312 days
420 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-0822020-01-0932020-01-1042020-01-115
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-12020-02-21-22020-02-22-12020-02-25-12020-02-26-2
dtype: int32
**
diff
**
datetime64[ns]
的序列进行
diff
后就能够得到
timedelta64[ns]
的序列
my_series = pd.Series(s.index)
my_series.head()
02020-01-0112020-01-0222020-01-0332020-01-0642020-01-07
dtype: datetime64[ns]
my_series.diff(1).head()
0 NaT
11 days
21 days
33 days
41 days
dtype: timedelta64[ns]
重采样
**
pd.resample
**
与groupby用法类似,是对时间序列分组计算的分组对象
s.resample('10D').mean().head()
2020-01-01-2.0000002020-01-11-3.1666672020-01-21-3.6250002020-01-31-4.0000002020-02-10-0.375000
Freq:10D, dtype: float64
重采样也可以使用**
apply
**
s.resample('10D').apply(lambda x:x.max()-x.min()).head()# 极差
2020-01-0132020-01-1142020-01-2142020-01-3122020-02-104
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-0108:26:35-12020-01-0108:27:52-12020-01-0108:29:09-22020-01-0108:30:26-32020-01-0108: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-0108:24:00-1.7500002020-01-0108:31:00-2.6000002020-01-0108:38:00-2.1666672020-01-0108:45:000.2000002020-01-0108:52:002.833333
Freq: 7T, dtype: float64
指定
origin
为
start
:最小时间戳开始依次增加
freq
进行分组
s.resample('7min', origin='start').mean().head()
2020-01-0108:26:35-2.3333332020-01-0108:33:35-2.4000002020-01-0108:40:35-1.3333332020-01-0108:47:351.2000002020-01-0108:54:353.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-310.4516132020-02-290.4482762020-03-310.5161292020-04-300.5666672020-05-310.451613
Freq: M, dtype: float64
s.resample('MS').mean().head()# 结果一样,但索引不同
2020-01-010.4516132020-02-010.4482762020-03-010.5161292020-04-010.5666672020-05-010.451613
Freq: MS, dtype: float64
归属人:我怕是有点打脑壳
严禁转载
版权归原作者 我怕是有点打脑壳 所有, 如有侵权,请联系我们删除。