Pandas基础教程学习(二)

第2章 Pandas索引

Pandas基础(一):单级索引、多级索引、索引设定、索引型函数、抽样

DataWhale第十二期组队学习:python Pandas

文章目录

  • 第2章 Pandas索引
    • 知识体系框架
    • 一、单级索引
      • 1.1 loc方法
      • 1.2 iloc方法
      • 1.3 []操作符
      • 1.4 布尔索引
      • 1.5 快速标量索引
      • 1.6 区间索引
    • 二、多级索引
      • 2.1 创建多级索引
      • 2.2 多层索引切片
      • 2.3 多层索引中的slice对象
      • 2.4 索引层的交换
    • 三、索引设定
      • 3.1 index_col参数
      • 3.2 reindex和reindex_like
      • 3.3 set_index和reset_index
      • 3.4 rename_axis和rename
    • 四、常用索引型函数
      • 4.1 where函数
      • 4.2 mask函数
      • 4.3 query函数
    • 五、重复元素处理
      • 5.1 duplicated方法
      • 5.2 drop_duplicates方法
    • 六、抽样函数
    • 七、问题与练习
    • 八、练习
      • 8.1 现有一份关于UFO的数据集,请解决下列问题
      • 8.2 现有一份关于口袋妖怪的数据集,请解决下列问题

知识体系框架

Pandas基础教程学习(二)_第1张图片

本文对应教程及数据表获取:joyful-pandas


一、单级索引

# 导入相关的依赖包
import pandas as pd
import numpy as np
pd.__version__ # 1.0.3
# 本文对应的数据表示例
df = pd.read_csv('data/table.csv',index_col='ID')
df.head(10)
本文所使用的数据表(前10行)
School Class Gender Address Height Weight Math Physics
ID
1101 S_1 C_1 M street_1 173 63 34.0 A+
1102 S_1 C_1 F street_2 192 73 32.5 B+
1103 S_1 C_1 M street_2 186 82 87.2 B+
1104 S_1 C_1 F street_2 167 81 80.4 B-
1105 S_1 C_1 F street_4 159 64 84.8 B+
1201 S_1 C_2 M street_5 188 68 97.0 A-
1202 S_1 C_2 F street_4 176 94 63.5 B-
1203 S_1 C_2 M street_6 160 53 58.8 A+
1204 S_1 C_2 F street_5 162 63 33.8 B
1205 S_1 C_2 F street_6 167 63 68.4 B-

单级索引有三种常用的方式.loc.iloc[],分别表示标签索引位置索引切片操作

1.1 loc方法

loc中使用切片全部包含右端点

"""行索引"""
# 单行索引
df.loc[1103] #选中index为1103的行
# 多行索引
df.loc[[1102,2304]]
df.loc[1304:]
df.loc[2402::-1]

"""列索引"""
# 单列索引
df.loc[:,'Height'] # 选中列名为Height的所有行
# 多列索引
df.loc[:,['Height','Math']] # 用列表表示两列
df.loc[:,'Height':'Math'] # 用切片表示三列

"""同时限定行列"""
# 行列索引
df.loc[1102:2401:3,'Height':'Math'] 

"""通过函数索引"""
# 函数式索引
df.loc[lambda x:x['Gender']=='M']
# 函数式索引-2
def f(x):
    return [1101,1103]
df.loc[f]

"""通过布尔变量索引"""
# 布尔索引
# []中是一个值为布尔类型的Series
df.loc[df['Address'].isin(['street_7','street_4'])]
# []中是一个值为布尔类型的List
df.loc[[True if i[-1]=='4' or i[-1]=='7' else False for i in df['Address'].values]]

1.2 iloc方法

loc不同,切片右端点不包含,且iloc是通过位置而不是通过标签来索引的

例如小明同学排在队伍第五个。那loc就是小明同学出列,而iloc就是第五位同学出列

"""行索引"""
# 单行索引
df.iloc[3] # 等同于df.loc[1104]
# 多行索引
df.iloc[3:5] # 等同于df.loc[[1104,1105]]

"""列索引"""
# 单列索引
df.iloc[:,3]
# 多列索引
df.iloc[:,7::-2]

"""行列同时索引"""
# 行列索引
df.iloc[3::4,7::-2]

"""函数式索引"""
# 通过函数进行索引
# 表达式中[3]返回DataFrame,3 返回Series,内容一样
df.iloc[lambda x:[3]].head()

因为iloc中的参数只能为整数或整数列表,所以不能使用布尔索引

1.3 []操作符

在行索引为浮点数的时候不要使用[]操作符,因为在Series中的浮点[]并不是位置比较,而是值比较

Series的[]操作
# 提取一列作为Series
s = pd.Series(df['Math'],index=df.index)
"""行索引"""
# 单行索引对于Series其实也就是单元素
s[1101] # 使用的是索引标签
# 多行索引
s[0:4] # 使用的是绝对位置的整数切片

"""函数式索引"""
# 函数式索引
s[lambda x: x.index[16::6]]

"""布尔索引"""
# []内是一个值为布尔类型的Series
s[s>80]
DataFrame的[]操作
"""行索引"""
# 绝对位置切片
df[1:2] # 取出1102这一行
df[3:5] # 多行索引
# 元素索引
row = df.index.get_loc(1102)
df[row:row+1] # 效果同上

"""列索引"""
# 单列索引
df['School']
# 多列索引
df[['School','Math']] # 传入一个列表

"""函数式索引"""
df[lambda x:['Math','Physics']]

"""布尔索引"""
# 传入的依然是一个Series
df[df['Gender']=='F']

1.4 布尔索引

"""利用与或非进行索引"""
df[(df['Gender']=='F')&(df['Address']=='street_2')]
df[(df['Math']>85)|(df['Address']=='street_7')]
df[~((df['Math']>75)|(df['Address']=='street_1'))]

"""在loc的相应位置中使用布尔列表选择"""
# 这里其实本质上是一个loc的行列索引
df_new = df.loc[df['Math']>60,(df[:8]['Address']=='street_6').values]

"""isin方法"""
# 包含返回True
df[df['Address'].isin(['street_1','street_4'])&df['Physics'].isin(['A','A+'])]
# all的作用等同“与”,1代表跨列方向
df[ df[['Address','Physics']].isin({
     'Address':['street_1','street_4'],'Physics':['A','A+']}).all(1)]

1.5 快速标量索引

当我们只要一个元素的时候,atiat方法能够提供更快的速度

display(df.at[1101,'School'])
display(df.loc[1101,'School'])
display(df.iat[0,0])
display(df.iloc[0,0])

%timeit df.at[1101,'School']  	# 6.24us
%timeit df.loc[1101,'School'] 	# 10.9us
%timeit df.iat[0,0]				# 6.85us
%timeit df.iloc[0,0]			# 11.2us

1.6 区间索引

"""利用interval_range方法"""
# 分出(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]五个区间
pd.interval_range(start=0,end=5) # closed参数控制左右闭合,默认左开右闭
# 分出步长为5的8个区间,(0, 5], (5, 10],……
pd.interval_range(start=0,periods=8,freq=5)

"""利用cut将数值类型的列变为区间"""
# 所有Math数值都变为形如(0, 40],(80, 100]的样子
# 结果为一个Series
math_interval = pd.cut(df['Math'],bins=[0,40,60,80,100])

"""对数值类型列可以区间索引"""
# 先在右侧添加一个新的数学成绩区间列
# 然后选出数学和数学成绩组成新df,再重设index为成绩区间
df_i = df.join(math_interval,rsuffix='_interval')[['Math','Math_interval']]\
            .reset_index().set_index('Math_interval')
# 包含该值就会被选中
df_i.loc[65] # 结果为一个DataFrame
df_i.loc[[65,90]]
# 若我要选出某个成绩区间,需要把分类变量转为区间变量
df_i[df_i.index.astype('interval').overlaps(pd.Interval(70, 85))].head()

二、多级索引

2.1 创建多级索引

# 直接创建元组
# 第一级为Upper:A,B,第二级为Lower:a,b,a,b
tuples = [('A','a'),('A','b'),('B','a'),('B','b')]
mul_index = pd.MultiIndex.from_tuples(tuples, names=('Upper', 'Lower'))
# 通过值与多级索引创建DataFrame
pd.DataFrame({
     'Score':['perfect','good','fair','bad']},index=mul_index)

# 利用zip创建元组
L1 = list('AABB')
L2 = list('abab')
tuples = list(zip(L1,L2))
mul_index = pd.MultiIndex.from_tuples(tuples, names=('Upper', 'Lower'))
pd.DataFrame({
     'Score':['perfect','good','fair','bad']},index=mul_index)

# 通过Array创建
# arrays自动转化为元组
arrays = [['A','a'],['A','b'],['B','a'],['B','b']]
mul_index = pd.MultiIndex.from_tuples(arrays, names=('Upper', 'Lower'))
pd.DataFrame({
     'Score':['perfect','good','fair','bad']},index=mul_index)

# 通过交叉乘积创建
L1 = ['A','B']
L2 = ['a','b']
pd.MultiIndex.from_product([L1,L2],names=('Upper', 'Lower'))

# 指定原有df中的列创建
df_using_mul = df.set_index(['Class','Address'])

2.2 多层索引切片

# 一般切片
df_using_mul.sort_index().index.is_lexsorted() # 检查是否排序
df_using_mul.sort_index().loc['C_2','street_5'] # 排序后切片
df_using_mul.sort_index().loc[('C_2','street_6'):('C_3','street_4')]
df_using_mul.sort_index().loc[('C_2','street_7'):'C_3']

# 通过元组构成列表
# 选出满足这二种组合的所有行
df_using_mul.sort_index().loc[[('C_2','street_7'),('C_3','street_2')]]

# 通过列表构成元组
# 选出满足这四种组合的所有行
df_using_mul.sort_index().loc[(['C_2','C_3'],['street_4','street_7']),:]

2.3 多层索引中的slice对象

# 行列各9种组合
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_s = pd.DataFrame(np.random.rand(9,9),index=mul_index1,columns=mul_index2)

# 使用索引Slice
idx=pd.IndexSlice
# 选出从B开始指定列大于0.3的行
# 选出列之和大于4的列
df_s.loc[idx['B':,df_s['D']['d']>0.3],idx[df_s.sum()>4]]

2.4 索引层的交换

# swaplevel方法(两层交换)
df_using_mul.swaplevel(i=1,j=0,axis=0).sort_index()

# reorder_levels方法(多层交换)
# 数字大小对应的是原来的位置
# 数字位置对应的是改变的位置
df_muls = df.set_index(['School','Class','Address'])
df_muls.reorder_levels([2,0,1],axis=0).sort_index()
# 不用数字也可以直接用Name
df_muls.reorder_levels(['Address','School','Class'],axis=0).sort_index()

三、索引设定

3.1 index_col参数

# index_col参数
# 设定索引列为Address、School
pd.read_csv('data/table.csv',index_col=['Address','School'])

3.2 reindex和reindex_like

# reindex和reindex_like
# 新表只有这四行,且1206这一行为NaN,因为原表没有1206
df.reindex(index=[1101,1203,1206,2402])
# 新表只有这三列,同理Average这一列为NaN
df.reindex(columns=['Height','Gender','Average'])

# 接上面,对缺失值填充
'''
    bfill表示用所在索引1206的后一个有效行填充
    ffill为前一个有效行
    nearest是指最近的
'''
df.reindex(index=[1101,1203,1206,2402],method='bfill')
df.reindex(index=[1101,1203,1206,2402],method='nearest')
# reindex_like
# 生成一个横纵索引完全与参数列表一致的DataFrame,数据使用被调用的表
# 数据为df_temp的值
df_temp = pd.DataFrame({
     'Weight':np.ones(5),
                        'Height':np.ones(5),
                        'ID':[1101,1104,1103,1105,1102]}).set_index('ID')
df_temp.reindex_like(df[0:5][['Weight','Height']])

# 新表中的1105是通过bfill填充的1106的值
df_temp = pd.DataFrame({
     'Weight':range(5),
                        'Height':range(5),
                        'ID':[1101,1104,1103,1106,1102]}).set_index('ID').sort_index()
df_temp.reindex_like(df[0:5][['Weight','Height']],method='bfill')

3.3 set_index和reset_index

# 使用表内列作为索引
df.set_index('Class')
# 在原来index基础上添加(右侧)
df.set_index('Class',append=True)
# 当使用与表长相同的列作为索引(先转化为Series)
df.set_index(pd.Series(range(df.shape[0])))
# 直接添加多级索引
df.set_index([pd.Series(range(df.shape[0])),pd.Series(np.ones(df.shape[0]))])
"""reset_index方法,主要是将索引重置"""
# 恢复到自然数索引
df.reset_index()
# level指定哪一层被reset,col_level指定set到哪一层
# 第二级行索引被重置,重置为列,级别为第二级列名
df_temp.reset_index(level=1,col_level=1)

3.4 rename_axis和rename

# rename_axis修改某一层的索引名,而不是索引标签
df_temp.rename_axis(index={
     'Lower':'LowerLower'},columns={
     'Big':'BigBig'})
# rename方法用于修改列或者行索引标签,而不是索引名
df_temp.rename(index={
     'A':'T'},columns={
     'e':'changed_e'})

Pandas基础教程学习(二)_第2张图片

四、常用索引型函数

4.1 where函数

# 不满足条件的行全部被设置为NaN
df.where(df['Gender']=='M')
# 丢弃掉为NaN的行
df.where(df['Gender']=='M').dropna()
# 不丢弃,填充进去
df.where(df['Gender']=='M',np.random.rand(df.shape[0],df.shape[1]))

4.2 mask函数

# 与上一个相反,满足条件的扔了
df.mask(df['Gender']=='M').dropna().head()
# 不丢弃,填充进去
df.mask(df['Gender']=='M',np.random.rand(df.shape[0],df.shape[1]))

4.3 query函数

# 利用布尔表达式查找
# 合法符号:行列索引名、字符串、and/not/or/&/|/~/not in/in/==/!=、四则运算符
df.query('(Address in ["street_6","street_7"])&(Weight>(70+10))&(ID in [1303,2304,2402])')

五、重复元素处理

5.1 duplicated方法

# 返回Class值下是否重复的布尔列表
df.duplicated('Class')

# keep参数下的duplicated
# 默认为first,即首次出现设为不重复(False)
# 若为last,则最后一次设为不重复(False)
# 若为False,则所有重复项为重复(True)
df.duplicated('Class',keep='last')
df.duplicated('Class',keep=False)

5.2 drop_duplicates方法

# 剔除重复项,默认保留第一个
df.drop_duplicates('Class')
# 剔除重复项,保留最后一个
df.drop_duplicates('Class',keep='last')
# 将多列共同视作一个多级索引(一个组合),比较重复项
df.drop_duplicates(['School','Class'])

六、抽样函数

抽样函数指的就是sample函数

# 随机抽取5个样本
df.sample(n=5)
# 按比例抽取样本
df.sample(frac=0.05)
# 是否允许一行可以采样多次
df.sample(n=df.shape[0],replace=True)
# axis选择要抽样的坐标轴,n为数量
df.sample(n=3,axis=1) # 随机抽取3列

# weights为样本权重,自动归一化
df.sample(n=3,weights=np.random.rand(df.shape[0]))
# 以某一列为权重,数学分高的容易抽中
df.sample(n=10,weights=df['Math'])

七、问题与练习

【问题一】 如何更改列或行的顺序?如何交换奇偶行(列)的顺序?

可以使用pandas.DataFrame.reindex改变顺序

奇偶行主要是设定参数

  1. 取出DataFrame的index
  2. 利用切片取出奇偶两个列表
  3. 通过zip组合成一个新的列表(注意奇偶顺序)
  4. [rv for r in zip(b,a) for rv in r]形成一个新的改变顺序的列表
  5. 作为参数给之前提到的改变行(列)顺序的函数

【问题二】 如果要选出DataFrame的某个子集,请给出尽可能多的方法实现。

行索引、列索引、函数索引、布尔索引、区间索引、快速标量索引、索引型函数、抽样函数

【问题三】 query函数比其他索引方法的速度更慢吗?在什么场合使用什么索引最高效?

按照正常行列索引会慢一点。如果逻辑比较的表达式比较复杂时用query更方便

【问题四】 单级索引能使用Slice对象吗?能的话怎么使用,请给出一个例子

idx=pd.IndexSlice
df.loc[:,idx['Address':]]

【问题五】 如何快速找出某一列的缺失值所在索引?

pandas.Series.isna,返回一个大小一样的Series,包含值为布尔变量,表明是否为NaN

【问题六】 索引设定中的所有方法分别适用于哪些场合?怎么直接把某个DataFrame的索引换成任意给定同长度的索引?

  • index_col:读取的时候设定多级索引
  • reindex和reindex_like:重新排序、按照已有dataFrame创建
  • set_index和reset_index:重新设定索引、重置某个索引
  • rename_axis和rename:重置索引名、重置索引标签

【问题七】 多级索引有什么适用场合?

一份数据含有多个特征,且特征也有递进或者包含的关系

【问题八】 什么时候需要重复元素处理

选出第一个(最后一个)不重复元素、判断数据的总共种类有多少、清洗数据、剔除重复项

八、练习

8.1 现有一份关于UFO的数据集,请解决下列问题

df1 = pd.read_csv('data/UFO.csv')
df1.head()

(a)在所有被观测时间超过60s的时间中,哪个形状最多?

df1[df1['duration (seconds)']>60]['shape'].value_counts().index[0] # light

(b)对经纬度进行划分:-180°至180°以30°为一个划分,-90°至90°以18°为一个划分,请问哪个区域中报告的UFO事件数量最多?

longitude_interval = pd.cut(df1['longitude'],bins=pd.interval_range(start=-180,end=180,freq=30))
latitude_interval = pd.cut(df1['latitude'],bins=pd.interval_range(start=-90,end=90,freq=18))

df_ll = df1.join(latitude_interval,rsuffix='_interval').join(longitude_interval,rsuffix='_interval')
df_ll.set_index(['latitude_interval','longitude_interval']).index.value_counts()
# ((36, 54], (-90, -60])   27891

8.2 现有一份关于口袋妖怪的数据集,请解决下列问题

df2 = pd.read_csv('data/Pokemon.csv')
df2.head()

(a)双属性的Pokemon占总体比例的多少?

df2['Type 2'].count()/df2.shape[0] # 0.5175

(b)在所有种族值(Total)不小于580的Pokemon中,非神兽(Legendary=False)的比例为多少?

df2[df2['Total']>=580]['Legendary'].value_counts(normalize='True')
# True     0.575221
# False    0.424779

(c)在第一属性为格斗系(Fighting)的Pokemon中,物攻排名前三高的是哪些?

df2[df2['Type 1']=='Fighting'].nlargest(3,'Attack')
# LucarioMega Lucario
# Conkeldurr
# Machamp

(d)请问六项种族指标(HP、物攻、特攻、物防、特防、速度)极差的均值最大的是哪个属性(只考虑第一属性,且均值是对属性而言)?

df2['range'] = df2.iloc[:,5:11].max(axis=1)-df2.iloc[:,5:11].min(axis=1)
attribute = df2[['Type 1','range']].set_index('Type 1')
max_range = 0
result = ''
for i in attribute.index.unique():
    temp = attribute.loc[i,:].mean()
    if temp.values[0] > max_range:
        max_range = temp.values[0]
        result = i
result

# Steel

(e)哪个属性(只考虑第一属性)的神兽占所有神兽比例最高?该属性神兽的种族值也是最高的吗?

df2.query('Legendary == True')['Type 1'].value_counts().index[0] # Psychic
attribute = df2.query('Legendary == True')[['Type 1','Total']].set_index('Type 1')
max_value = 0
result = ''
for i in attribute.index.unique()[:-1]:
    temp = attribute.loc[i,:].mean()
    if temp[0] > max_value:
        max_value = temp[0]
        result = i
result

# Normal

你可能感兴趣的:(Pandas,python,数据分析)