Python-S02E12DataFrame数据选取方法

545 阅读9分钟
原文链接: zhuanlan.zhihu.com

python,不管你懂没懂,反正妹子是搞懂了

转载请注明:知乎专栏《给妹子讲python》--酱油哥

【要点抢先看】

1.对列标签进行字典形式的取值访问,获取Series对象
2.使用字典语法进行列的扩充
3.使用values属性,获取ndarray二维数组类型,可以使用相同操作方法
4.loc、iloc、ix三种索引器,支持行和列的索引、分片灵活操作
5.DataFrame的条件过滤操作
6.DataFrame具体值的修改

这一集,我们来说DataFrame对象的数据选取方法。同样,我们使用类比法进行学习,前面我们提到过,DataFrame可以看作是若干Series对象构成的字典,也可以看做是一个二维数组

我们先类比字典,讨论如何获取DataFrame的指定数据,我们这一集例子中操作的就是下面这个DataFrame数据对象。

import pandas as pd

area = pd.Series({'California':423967,'Texas':695662,
                  'New York':141297,'Floriade':170312,
                  'Illinois':149995})

pop = pd.Series({'California':38332521,'Texas':26448193,
                  'New York':19651127,'Floriade':19552860,
                  'Illinois':12882135})

data = pd.DataFrame({'area':area, 'pop':pop})
print(data)

              area       pop
California  423967  38332521
Floriade    170312  19552860
Illinois    149995  12882135
New York    141297  19651127
Texas       695662  26448193

由于DataFrame本质上反应的是列标签与Series的映射关系,所以对列名进行字典形式的取值,可以获取相应的Series列数据:

data = pd.DataFrame({'area':area, 'pop':pop})
print(data['area'])

California    423967
Floriade      170312
Illinois      149995
New York      141297
Texas         695662
Name: area, dtype: int64

和这种字典形式访问效果相同的是属性形式的访问方法,即data.area得到的结果和本例相同,不过属性访问方式的限制要多一些,例如如果列标签不是纯字符串或者恰巧与一些方法名重名,则不能使用该方法。

当然,我们同样可以使用字典形式的语法进行列扩充,可以给一个新列赋值一个标量,也可以对其赋值一个向量数组:

data = pd.DataFrame({'area':area, 'pop':pop})
data['a'] = [1,2,3,4,5]
data['b'] = 0
print(data)

              area       pop  a  b
California  423967  38332521  1  0
Floriade    170312  19552860  2  0
Illinois    149995  12882135  3  0
New York    141297  19651127  4  0
Texas       695662  26448193  5  0

不过在日常使用中,某一列可能是其他两列通过算术运算得到的结果,例如,我们通过人口除以面积,可以得到每个州的人口密度,需要我们一个州一个州的循环计算吗?显然不需要,看看我们怎么做:

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data)

             area       pop     density
California  423967  38332521   90.413926
Floriade    170312  19552860  114.806121
Illinois    149995  12882135   85.883763
New York    141297  19651127  139.076746
Texas       695662  26448193   38.018740

我们再看看,如果类比ndarray二维数组,我们如何处理DataFrame数据获取的需求。

首先我们可以用values属性,可以获得一个二维ndarray二维数组:

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data.values)

[[  4.23967000e+05   3.83325210e+07   9.04139261e+01]
 [  1.70312000e+05   1.95528600e+07   1.14806121e+02]
 [  1.49995000e+05   1.28821350e+07   8.58837628e+01]
 [  1.41297000e+05   1.96511270e+07   1.39076746e+02]
 [  6.95662000e+05   2.64481930e+07   3.80187404e+01]]

通过values属性可以获取二维ndarray数组形式的数据之后,我们自然可以像操作二维数组一样,对其进行行索引和值的索引。不过单独的列索引只能用刚刚讲过的传递列标签作为字典键的形式获取。

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data.values[2])
print(data.values[1][1])
print(data.values[1:][:2])

[  1.49995000e+05   1.28821350e+07   8.58837628e+01]

19552860.0

[[  1.70312000e+05   1.95528600e+07   1.14806121e+02]
 [  1.49995000e+05   1.28821350e+07   8.58837628e+01]]

自然而然我们会想,带有DataFrame本身标签结构的分片操作该如何进行,毕竟ndarray没有行、列信息,而这些信息恰是DataFrame的重要组成部分。

由于DataFrame的行和列的获取方法不太统一,所以DataFrame提供了三种索引器的方法,很好的满足了我们的需求。

iloc索引,行列都使用隐式索引,我们可以像操作ndarray数组一样,对DataFrame数据类型进行索引分片操作:

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data.iloc[:2,1:2])

                 pop
California  38332521
Floriade    19552860

从这里我们可以看到,无论是行还是列,都采用隐式的整数索引进行分片,规则都是左闭右开。

loc索引,则是采用显式的标签值索引进行分片,规则是左右都取:

import pandas as pd

area = pd.Series({'California':423967,'Texas':695662,
                  'New York':141297,'Floriade':170312,
                  'Illinois':149995})

pop = pd.Series({'California':38332521,'Texas':26448193,
                  'New York':19651127,'Floriade':19552860,
                  'Illinois':12882135})

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data)
print(data.loc[:'Floriade', 'area':'pop'])

              area       pop     density
California  423967  38332521   90.413926
Floriade    170312  19552860  114.806121
Illinois    149995  12882135   85.883763
New York    141297  19651127  139.076746
Texas       695662  26448193   38.018740

              area       pop
California  423967  38332521
Floriade    170312  19552860

有一点我们留心一下,就是我们在生成Series对象的时候,传入的各个州的顺序和生成的DataFrame所展现的顺序是不同的,DataFrame行的顺序是按照行标签字母顺序排列的。但是请注意,列的顺序却和我们指定的传入顺序一致

我们在实际使用的时候会发现,列的名称往往是已知的,因为他是我们需要观测的各种属性,而行标签的名字往往是未知的,因为他一般是人名、日期、地区名等等这些读入的待分析的个体数据。

那么实际中我们希望用隐式整数索引进行行的分片,利用显式标签名称进行列的分片,可以办到么?用ix索引器就能办到。

比如我们利用ix索引器观察前三行数据的area和pop属性:

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data)
print(data.ix[:3, 'area':'pop'])

              area       pop     density
California  423967  38332521   90.413926
Floriade    170312  19552860  114.806121
Illinois    149995  12882135   85.883763
New York    141297  19651127  139.076746
Texas       695662  26448193   38.018740

              area       pop
California  423967  38332521
Floriade    170312  19552860
Illinois    149995  12882135

那如果我们对于列的选取,不是想选取连续的分片,而是选取指定列,如何做到?稍作修改即可:

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data)
print(data.ix[:3, ['area','density']])

              area       pop     density
California  423967  38332521   90.413926
Floriade    170312  19552860  114.806121
Illinois    149995  12882135   85.883763
New York    141297  19651127  139.076746
Texas       695662  26448193   38.018740

              area     density
California  423967   90.413926
Floriade    170312  114.806121
Illinois    149995   85.883763

当然,分片操作还有一种简单的方法,如果是只对行进行分片操作,那么就不需要索引器了。

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data['Floriade':'New York'])
print(data[1:3])

            area       pop     density
Floriade  170312  19552860  114.806121
Illinois  149995  12882135   85.883763
New York  141297  19651127  139.076746
            area       pop     density
Floriade  170312  19552860  114.806121
Illinois  149995  12882135   85.883763

看到没,如果[ ]中只有一个值,那就是对列进行索引,如果是分片那就是对行进行操作了

相应的,条件过滤操作也来说说:

我们想看看,人口密度大于100的州

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data[data['density'] > 100])

            area       pop     density
Floriade  170312  19552860  114.806121
New York  141297  19651127  139.076746

通过这种方法得到的我们要的结果,返回的是各州的整行数据。

如果我们想看看人口密度大于100的州,且只想看看他的面积和人口密度怎么办?这个结合之前介绍的索引器,不难办到:

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
print(data.loc[data['density'] > 100, ['area','density']])

            area     density
Floriade  170312  114.806121
New York  141297  139.076746

如果想要修改DataFrame中的某个值,使用任何一种索引器方法定位到具体的一个数据项,即可办到:

data = pd.DataFrame({'area':area, 'pop':pop})
data['density'] = data['pop'] / data['area']
data.loc['Floriade', 'area'] = 9999999
data.iloc[4,1] = 8888888
print(data)

               area       pop     density
California   423967  38332521   90.413926
Floriade    9999999  19552860  114.806121
Illinois     149995  12882135   85.883763
New York     141297  19651127  139.076746
Texas        695662   8888888   38.018740

【妹子说】往往这种罗列API方法的内容都是枯燥不易记忆的,不过通过进行和别的已知数据类型进行对比讲解,顿时感觉清晰了不少,后面请继续加油讲解吧~