Pandas(2)

时间秒杀一切 提交于 2020-02-05 18:56:29

《Python数据科学手册》读书笔记

数据取值与选择

我将从简单的一维 Series 对象开始, 然后再用比较复杂的二维
DataFrame 对象进行演示。

Series数据选择方法

如前所述, Series 对象与一维 NumPy 数组和标准 Python 字典在许多
方面都一样。 只要牢牢记住这两个类比, 就可以帮助我们更好地理解
Series 对象的数据索引与选择模式。

将Series看作字典

和字典一样, Series 对象提供了键值对的映射:

import pandas as pd
data = pd.Series([0.25, 0.5, 0.75, 1.0],
                 index=['a', 'b', 'c', 'd'])
data
a    0.25
b    0.50
c    0.75
d    1.00
dtype: float64
data['b']
0.5

还可以用 Python 字典的表达式和方法来检测键 / 索引和值:

'a' in data
True
data.keys()
Index(['a', 'b', 'c', 'd'], dtype='object')
list(data.items())
[('a', 0.25), ('b', 0.5), ('c', 0.75), ('d', 1.0)]

Series 对象还可以用字典语法调整数据。 就像你可以通过增加新
的键扩展字典一样, 你也可以通过增加新的索引值扩展 Series:

data['e'] = 1.25
data
a    0.25
b    0.50
c    0.75
d    1.00
e    1.25
dtype: float64

将Series看作一维数组

Series 不仅有着和字典一样的接口, 而且还具备和 NumPy 数组一
样的数组数据选择功能, 包括索引、 掩码、 花哨的索引等操作,
具体示例如下所示:

# 将显示索引作为切片
data['a':'c']
a    0.25
b    0.50
c    0.75
dtype: float64
# 将隐式整数索引作为切片
data[0:2]
a    0.25
b    0.50
dtype: float64
# 掩码
data[(data > 0.3) & (data < 0.8)]
b    0.50
c    0.75
dtype: float64
# 花哨的索引
data[['a', 'e']]
a    0.25
e    1.25
dtype: float64

需要注意的是, 当使用
显式索引(即 data[‘a’:‘c’]) 作切片时, 结果包含最后一个索
引; 而当使用隐式索引(即 data[0:2]) 作切片时, 结果不包含
最后一个索引。

索引器: loc、 iloc和ix

这些切片和取值的习惯用法经常会造成混乱。 例如, 如果你的
Series 是显式整数索引, 那么 data[1] 这样的取值操作会使用显
式索引, 而 data[1:3] 这样的切片操作却会使用隐式索引。

data = pd.Series(['a', 'b', 'c'], index=[1, 3, 5])
data
1    a
3    b
5    c
dtype: object
# 取值操作是显式索引
data[1]
'a'
# 切片操作是隐式索引
data[1:3]
3    b
5    c
dtype: object

由于整数索引很容易造成混淆, 所以 Pandas 提供了一些索引器
(indexer) 属性来作为取值的方法。

  1. 第一种索引器是 loc 属性, 表示取值和切片都是显式的:
data.loc[1]
'a'
data.loc[1:3]
1    a
3    b
dtype: object
  1. 第二种是 iloc 属性, 表示取值和切片都是 Python 形式的隐式索
    引:
data.iloc[1]
'b'
data.iloc[1:3]
3    b
5    c
dtype: object

Python 代码的设计原则之一是“显式优于隐式”。 使用 loc 和 iloc
可以让代码更容易维护, 可读性更高。 特别是在处理整数索引的对
象时。

  1. 第三种取值属性是 ix, 它是前两种索引器的混合形式, 在 Series
    对象中 ix 等价于标准的 [ ](Python 列表) 取值方式。 ix 索引器
    主要用于 DataFrame 对象,后面再具体介绍

DataFrame数据选择方法

DataFrame 在有些方面像二维或结构化数组, 在有些方面
又像一个共享索引的若干 Series 对象构成的字典。 这两种类比可以帮
助我们更好地掌握这种数据结构的数据选择方法。

  1. 将DataFrame看作字典

第一种类比是把 DataFrame 当作一个由若干 Series 对象构成的
字典。 让我们用之前的美国五州面积与人口数据来演示:

area = pd.Series({'California': 423967, 'Texas': 695662,
'New York': 141297, 'Florida': 170312,
'Illinois': 149995})
pop = pd.Series({'California': 38332521, 'Texas': 26448193,
'New York': 19651127, 'Florida': 19552860,
'Illinois': 12882135})
data = pd.DataFrame({'area':area, 'pop':pop})
data
area pop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
Florida 170312 19552860
Illinois 149995 12882135

两个 Series 分别构成 DataFrame 的一列, 可以通过对列名进行
字典形式(dictionary-style) 的取值获取数据:

data['area']
California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
Name: area, dtype: int64

同样, 也可以用属性形式(attribute-style) 选择纯字符串列名的数
据:

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

对同一个对象进行属性形式与字典形式的列数据, 结果是相同的:

data.area is data['area']
True

虽然属性形式的数据选择方法很方便, 但是它并不是通用的。 如果
列名不是纯字符串, 或者列名与 DataFrame 的方法同名, 那么就
不能用属性索引。 例如, DataFrame 有一个 pop() 方法, 如果用
data.pop 就不会获取 ‘pop’ 列, 而是显示为方法:

data.pop is data['pop']
False

另外, 还应该避免对用属性形式选择的列直接赋值(即可以用
data[‘pop’] = z, 但不要用 data.pop = z) 。

可以用字典形式的语法调整DataFrame对
象, 如果要增加一列可以这样做:

data['density'] = data['pop'] / data['area']
data
area pop density
California 423967 38332521 90.413926
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
  1. 将DataFrame看作二维数组

可以把 DataFrame 看成是一个增强版的二维数组,
用 values 属性按行查看数组数据

data.values
array([[4.23967000e+05, 3.83325210e+07, 9.04139261e+01],
       [6.95662000e+05, 2.64481930e+07, 3.80187404e+01],
       [1.41297000e+05, 1.96511270e+07, 1.39076746e+02],
       [1.70312000e+05, 1.95528600e+07, 1.14806121e+02],
       [1.49995000e+05, 1.28821350e+07, 8.58837628e+01]])

可以把许多数组操作方式用在 DataFrame 上。
例如, 可以对 DataFrame 进行行列转置:

data.T
California Texas New York Florida Illinois
area 4.239670e+05 6.956620e+05 1.412970e+05 1.703120e+05 1.499950e+05
pop 3.833252e+07 2.644819e+07 1.965113e+07 1.955286e+07 1.288214e+07
density 9.041393e+01 3.801874e+01 1.390767e+02 1.148061e+02 8.588376e+01

通过字典形式对列进行取值显然太过于极限, 尤其是当我们在 DataFrame 数组中
使用单个行索引获取一行数据时:

 data.values[0]
array([4.23967000e+05, 3.83325210e+07, 9.04139261e+01])

而获取一列数据就需要向 DataFrame 传递单个列索引:

data['area']
California    423967
Texas         695662
New York      141297
Florida       170312
Illinois      149995
Name: area, dtype: int64


面介绍过的 Pandas 索引器 loc、 iloc 和 ix 了。 通过 iloc 索引
器, 我们就可以像对待 NumPy 数组一样索引 Pandas 的底层数组
(Python 的隐式索引) , DataFrame 的行列标签会自动保留在结
果中:

 data.iloc[:3, :2]
area pop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
data.loc[:'Illinois', :'pop']
area pop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
Florida 170312 19552860
Illinois 149995 12882135

使用 ix 索引器可以实现一种混合效果

data.ix[:3, :'pop']
C:\ProgramData\Anaconda3\lib\site-packages\ipykernel_launcher.py:1: DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated
  """Entry point for launching an IPython kernel.
area pop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127

需要注意的是, ix 索引器对于整数索引的处理和之前在 Series 对
象中介绍的一样, 都容易让人混淆。

任何用于处理 NumPy 形式数据的方法都可以用于这些索引器。 例
如, 可以在 loc 索引器中结合使用掩码与花哨的索引方法:

 data.loc[data.density > 100, ['pop', 'density']]
pop density
New York 19651127 139.076746
Florida 19552860 114.806121

任何一种取值方法都可以用于调整数据, 这一点和 NumPy 的常用
方法是相同的:

data.iloc[0, 2] = 90
data
area pop density
California 423967 38332521 90.000000
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
  1. 其他取值方法

如果对单个标签取值
就选择列, 而对多个标签用切片就选择行:

data['Florida':'Illinois']
area pop density
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763

切片也可以不用索引值, 而直接用行数来实现:

data[1:3]
area pop density
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746

与之类似, 掩码操作也可以直接对每一行进行过滤, 而不需要使用
loc 索引器:

data[data.density > 100]
area pop density
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121

Pandas数值运算方法

通用函数: 保留索引

因为 Pandas 是建立在 NumPy 基础之上的, 所以 NumPy 的通用函数同
样适用于 Pandas 的 Series 和 DataFrame 对象。

import pandas as pd
import numpy as np
rng = np.random.RandomState(42)
ser = pd.Series(rng.randint(0, 10, 4))
ser
0    6
1    3
2    7
3    4
dtype: int32
df = pd.DataFrame(rng.randint(0, 10, (3, 4)),
columns=['A', 'B', 'C', 'D'])
df
A B C D
0 6 9 2 6
1 7 4 3 7
2 7 2 5 4

如果对这两个对象的其中一个使用 NumPy 通用函数, 生成的结果是另
一个保留索引的 Pandas 对象:

np.exp(ser)
0     403.428793
1      20.085537
2    1096.633158
3      54.598150
dtype: float64
np.sin(df * np.pi / 4)
A B C D
0 -1.000000 7.071068e-01 1.000000 -1.000000e+00
1 -0.707107 1.224647e-16 0.707107 -7.071068e-01
2 -0.707107 1.000000e+00 -0.707107 1.224647e-16

通用函数: 索引对齐

当在两个 Series 或 DataFrame 对象上进行二元计算时, Pandas 会在
计算过程中对齐两个对象的索引。 当你处理不完整的数据时, 这一点非
常方便。

  1. Series索引对齐

来看一个例子, 假如要整合两个数据源的数据, 其中一个是美国
面积最大的三个州的面积数据, 另一个是美国人口最多的三个州的
人口数据:

area = pd.Series({'Alaska': 1723337, 'Texas': 695662,
                    'California': 423967}, name='area')
population = pd.Series({'California': 38332521, 'Texas': 26448193,
                        'New York': 19651127}, name='population')

来看看如果用人口除以面积会得到什么样的结果:

population / area
Alaska              NaN
California    90.413926
New York            NaN
Texas         38.018740
dtype: float64

对于缺失位置的数据, Pandas 会用 NaN 填充, 表示“此处无数”。 这种索引对齐方式是通过 Python 内置的集合运算规则实现
的, 任何缺失值默认都用 NaN 填充:

A = pd.Series([2, 4, 6], index=[0, 1, 2])
B = pd.Series([1, 3, 5], index=[1, 2, 3])
A + B
0    NaN
1    5.0
2    9.0
3    NaN
dtype: float64

如果用 NaN 值不是我们想要的结果, 那么可以用适当的对象方法代替运算符。 例如, A.add(B) 等价于 A + B, 也可以设置参数自定
义 A 或 B 缺失的数据:

A.add(B, fill_value=0)
0    2.0
1    5.0
2    9.0
3    5.0
dtype: float64
  1. DataFrame索引对齐

在计算两个 DataFrame 时, 类似的索引对齐规则也同样会出现在
共同(并集) 列中:

A = pd.DataFrame(rng.randint(0, 20, (2, 2)),
columns=list('AB'))
A
A B
0 1 11
1 5 1
B = pd.DataFrame(rng.randint(0, 10, (3, 3)),
columns=list('BAC'))
B
B A C
0 4 0 9
1 5 8 0
2 9 2 6
A + B
A B C
0 1.0 15.0 NaN
1 13.0 6.0 NaN
2 NaN NaN NaN

两个对象的行列索引可以是不同顺序的, 结果的索引会
自动按顺序排列。 在 Series 中,可以通过运算符方法的
fill_value 参数自定义缺失值。这里,用 A 中所有值的均值来填充缺失值(计算 A 的均值需要用 stack 将二维数组压缩成
一维数组) :

fill = A.stack().mean()
A.add(B, fill_value=fill)
A B C
0 1.0 15.0 13.5
1 13.0 6.0 4.5
2 6.5 13.5 10.5

Python运算符与Pandas方法的映射关系

Python运算符 Pandas方法
+ add()
- sub()、 subtract()
* mul()、 multiply()
/ truediv()、 div()、 divide()
// floordiv()
% mod()
** pow()

通用函数: DataFrame与Series的运算

DataFrame 和 Series 的运算规则, 与
NumPy 中二维数组与一维数组的运算规则是一样的。 来看一个常见运
算, 让一个二维数组减去自身的一行数据:

A = rng.randint(10, size=(3, 4))
A
array([[3, 8, 2, 4],
       [2, 6, 4, 8],
       [6, 1, 3, 8]])
 A - A[0]
array([[ 0,  0,  0,  0],
       [-1, -2,  2,  4],
       [ 3, -7,  1,  4]])

根据 NumPy 的广播规则 , 让二维数组减自身的
一行数据会按行计算。

在 Pandas 里默认也是按行运算的:

df = pd.DataFrame(A, columns=list('QRST'))
df - df.iloc[0]
Q R S T
0 0 0 0 0
1 -1 -2 2 4
2 3 -7 1 4

如果想按列计算,要通过
axis 参数设置:

df.subtract(df['R'], axis=0)
Q R S T
0 -5 0 -6 -4
1 -4 0 -2 2
2 5 0 2 7

DataFrame / Series 的运算与前面介绍的运算一样, 结果的
索引都会自动对齐

halfrow = df.iloc[0, ::2]
halfrow
Q    3
S    2
Name: 0, dtype: int32
df - halfrow
Q R S T
0 0.0 NaN 0.0 NaN
1 -1.0 NaN 2.0 NaN
2 3.0 NaN 1.0 NaN
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!