[关闭]
@jk88876594 2017-08-15T14:00:50.000000Z 字数 4829 阅读 12294

DataFrame——数据选取与筛选

阿雷边学边教python数据分析第3期——pandas与numpy


一.数据选取

  • 按索引标签选取(loc做法)
  • 按索引位置选取(iloc做法)

Q1:选取第1行的数据(选取单行数据)

(1)按索引标签选取(loc做法)

  1. df.loc[0] #返回的是Series
  1. df.loc[[0]] #如果在里面多加一个方括号,那么返回的是DataFrame

(2)按索引位置选取(iloc做法)

  1. df.iloc[0] #返回的是Series
  1. df.iloc[[0]] #如果在里面多加一个方括号,那么返回的是DataFrame

问题来了:为什么在这里loc和iloc得到的结果是一样的?
下面会解释

Q2:选取第2到第5行的数据(选取连续行的数据)

(1)按索引标签选取(loc做法)

  1. df.loc[1:4]

你可能产生了一个疑问:不是说切片的末端是取不到的吗,也就是4这个索引所指向的第5行应该是取不到的
这是因为loc是按照索引标签来选取数据的,而不是根据位置来选取,举个例子:

  1. #以姓名这一列作为行索引
  2. df_name = df.set_index("姓名")
  3. df_name

如果我要返回第2行到第5行的数据,该怎么做呢?

  1. df_name.loc[1:4] #如果按照刚刚的写法,就会出错
  1. #因为loc是按照索引标签选取的,按照下面这种写法就对了
  2. df_name.loc["Ivysaur":"Charmander"]

所以说,之前写的df.loc[1:4]能返回第2到第5行的数据,只是恰好因为索引号是默认生成的数字索引,1对应的就是第2行的索引,4对应的是第5行的索引,1:4代表的是从第2行到第5行的索引标签,本质是和现在的"Ivysaur":"Charmander"一样的,都是代表索引标签,而不是位置。所以按照这种索引标签来选取数据的方法是能够取到末端的数据的.现在回去看刚刚Q1的问题你就明白了为什么在Q1里loc和iloc得到的结果是一样的,因为df数据的索引标签和位置恰好一样。

(2)按索引位置选取(iloc做法)

  1. df.iloc[1:5]

我们需要返回的是第2行到第5行,因此对应的索引位置是1:4,但是由于iloc是按照位置来选取数据的,遵循左闭右开的原则,因此末端索引是取不到的,那么末端就需要再加1,这样就能确保第5行能取到了,而取不到第6行

为了能更直观地体现出loc和iloc的区别,接下来以df_name为示例数据

  1. #示例数据
  2. df_name

Q3:选取第2行,第4行,第7行,第10行的数据(选取特定行的数据)

(1)按索引标签选取(loc做法)

  1. df_name.loc[["Ivysaur","VenusaurMega Venusaur","Charizard","Squirtle"]]

(2)按索引位置选取(iloc做法)

  1. df_name.iloc[[1,3,6,9]]

Q4:选取攻击力列(选取单列的数据)

(1)直接方括号+列名

  1. #直接方括号输入列名即可,推荐这种方法
  2. df_name["攻击力"]
  3. #返回的是一个Series
  1. df_name[["攻击力"]]
  2. #返回的是一个DataFrame

(2)按索引标签选取(loc做法)

  1. #虽然用loc也能提取单列,但是显得不够简洁
  2. df_name.loc[:,["攻击力"]]

(3)按索引位置选取(iloc做法)

  1. df_name.iloc[:,[4]]

(4)点号选取法

  1. #也可以通过点号选取列
  2. df_name.攻击力

点号提取列的这种方法的优点是:写法比较简洁快速,缺点是如果列名和关键字重复了就无法提取了,因为点号调用的是对象,python无法判断出名字一样的列名和关键字

  1. #举个例子说明,新增一列class,值为1
  2. df_name["class"] = 1
  3. df_name
  1. df_name.class
  2. #由于class是python的关键字,而点号选取列实质上是在调用对象,本身列名class和关键字class重叠了,导致无法调用成功

Q5:选取类型1列到攻击力列的所有数据(选取连续列的数据)

(1)按索引标签选取(loc做法)

  1. df_name.loc[:,"类型1":"攻击力"]

(2)按索引位置选取(iloc做法)

  1. df_name.iloc[:,:4]

Q6:选取“类型2”列,攻击力列,防御力列的所有数据(选取特定列的数据)

(1)方括号+列名

  1. #用方括号+列名来直接提取,这种方式比较简洁
  2. df_name[["类型2","攻击力","防御力"]]

(2)按索引标签选取(loc做法)

  1. df_name.loc[:,["类型2","攻击力","防御力"]]

(3)按索引位置选取(iloc做法)

  1. df_name.iloc[:,[1,4,5]]

Q7:选取第3行到第8行,类型1列到攻击力列(选取部分行部分列的数据)

(1)按索引标签选取(loc做法)

  1. df_name.loc["Venusaur":"CharizardMega Charizard X","类型1":"攻击力"]

(2)按索引位置选取(iloc做法)

  1. df_name.iloc[2:7,:5]

拓展:ix选取数据的做法

也许有的人觉得一会索引标签来选取,一会索引位置来选取,记起来好麻烦,那么,有没有一种万能的办法呢?
确实有种相对较万能的办法,就是 ix,它既能够按照索引标签选取数据,也能够按照索引位置选取数据

ix的工作原理:根据索引的类型分2种情况:

1.索引为整数标签,那么按照索引标签选取行数据,不能按照索引位置选取行数据,列数据既能通过标签选取也能通过位置选取。
2.当索引为非整数标签(如字符串标签),那么可以用索引标签选取行数据,也可以按照索引位置选取行数据,列数据既能通过标签选取也能通过位置选取。

个人建议:还是使用loc和iloc来选取数据较好,因为分工明确,loc通过索引标签选取数据,iloc通过索引位置选取数据,所以你会很清除地知道你是在索引标签上操作还是在索引位置上操作,不会觉得混乱。我并不是特别建议使用ix,ix会让你觉得很混乱,也会让别人看你的代码时会在想:到底现在是在索引标签上操作还是位置上操作?这就增加了一个判断的过程。

二.数据筛选

  1. #示例数据
  2. df.head()

Q1:选取出攻击力大于100的所有数据

1.loc筛选

  1. df.loc[df["攻击力"] > 100]

2.query筛选

  1. df.query("攻击力 > 100")

Q2:选出攻击力大于100且防御力大于100的数据,并且列只要姓名、攻击力、防御力

1.loc筛选

  1. df.loc[(df["攻击力"] > 100) & (df["防御力"] > 100),["姓名","攻击力","防御力"]]

2.query筛选

  1. df.query("攻击力 > 100 & 防御力 > 100")[["姓名","攻击力","防御力"]]

Q3:选出类型1为Grass的所有数据

1.loc筛选

  1. #做法1
  2. df.loc[df["类型1"] == "Grass"]
  1. #做法2
  2. df.loc[df["类型1"].isin(["Grass"])]

2.query筛选

  1. df.query("类型1 == 'Grass'")

loc筛选和query筛选的区别:
1.query的写法更简洁,免去了写数据框名称的步骤,节省时间
2.query不能引入变量,loc可以引入变量

三.多重索引

Q1:什么是多重索引

指2层或2层以上的索引

为什么会用到多重索引呢?
因为有时候需要通过多个维度来查看数据

Q2:如何创建多重索引

通过set_index([第一层索引,第二层索引,...])的方式创建含有多重索引的数据

  1. #当我们要以字符串列作为索引列时,要保证这列为字符串格式
  2. df[["类型1","类型2"]] = df[["类型1","类型2"]].astype("str")
  3. #创建一个具有2重索引的数据作示例
  4. df_pokemon = df.set_index(["类型1","类型2"])
  5. df_pokemon

参数介绍:
drop:是指该列被指定为索引后,是否删除该列,默认为True,即删除该列。如果改成False,则多重索引在数据集的列中也会保留
append:指定是否保留原索引,默认为False,即不保留,如果改成True,则保留原索引
inplace:指是否在源数据的基础上修改,默认为False,即不修改,返回一个新的数据框,如果改成True,则直接在源数据上修改

level介绍:

  1. #获取第一层索引
  2. df_pokemon.index.get_level_values(0)
  1. #获取第二层索引
  2. df_pokemon.index.get_level_values(1)
  1. #交换level
  2. df_pokemon.swaplevel()

Q3:如何通过多重索引选取数据

1.先对索引进行升序排序

  1. df_pokemon.sort_index(inplace=True)

为什么要对索引升序排序?
因为如果没有对索引进行升序排序的话,在多重索引选取数据的过程中无法通过切片选取数据,切片是由小到大取的,例如字符串a→z,数字0→100,所以在对索引进行升序后,才能正确地切片选取数据

2.用loc通过多重索引选取数据

  1. #取出第一索引列中值为Bug的所有数据
  2. df_pokemon.loc["Bug"]
  1. #取出第一索引列为Bug,第二索引列为Poison的所有数据
  2. df_pokemon.loc[("Bug","Poison")]
  1. #选出第一索引列为Bug到Grass的所有数据
  2. df_pokemon.loc[slice("Bug","Grass")]
  1. #选出第一索引列为Bug到Grass,且第二索引列为Electric的所有数据
  2. df_pokemon.loc[(slice("Bug","Grass"),"Electric"),:]

当想要取某一列索引下的全部数据时就需要用slice(None)

  1. #取第二索引列为Electric的所有数据
  2. df_pokemon.loc[(slice(None),"Electric"),:]
  1. #取第二索引列为Electric和Fire,且列为姓名到攻击力的所有数据
  2. df_pokemon.loc[(slice(None),["Electric","Fire"]),"姓名":"攻击力"]

前面的做法有一点繁琐,还有更简洁的做法,可以不用去写slice

  1. idx = pd.IndexSlice
  1. #取第二索引列为Electric和Fire,且列为姓名到攻击力的所有数据
  2. df_pokemon.loc[idx[:,["Electric","Fire"]],"姓名":"攻击力"]
  1. #取第二索引为Electric到Fire的所有数据
  2. df_pokemon.loc[idx[:,"Electric":"Fire"],:]
  1. #取第一索引为Bug到Grass,且第二索引为Electric到Fire的所有数据
  2. df_pokemon.loc[idx["Bug":"Grass","Electric":"Fire"],:]

还有一个函数xs可以通过level指定索引,然后去选取数据

  1. #选取第一索引列即类型1为Bug的所有数据
  2. df_pokemon.xs("Bug",level=0)
  1. #选取第二索引列为Electric的所有数据
  2. df_pokemon.xs("Electric",level=1)
  1. #选取第二索引列为Electric的所有数据,并且保留第二索引列
  2. df_pokemon.xs("Electric",level=1,drop_level=False)
  1. #选取第一索引列为Bug和Dark,第二索引列为Electric和Fire的所有数据
  2. df_pokemon.xs(("Bug","Electric"),level=(0,1))
  1. #level也可以是索引列名
  2. df_pokemon.xs(("Bug","Electric"),level=(["类型1","类型2"]))

xs在每个索引列上选择的标签只能是一个,所以做不到切片标签的选取

N6UQE41GIJULSCTJF4D}@MV.png-263.8kB

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注