文章目录
《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) 属性来作为取值的方法。
- 第一种索引器是 loc 属性, 表示取值和切片都是显式的:
data.loc[1]
'a'
data.loc[1:3]
1 a
3 b
dtype: object
- 第二种是 iloc 属性, 表示取值和切片都是 Python 形式的隐式索
引:
data.iloc[1]
'b'
data.iloc[1:3]
3 b
5 c
dtype: object
Python 代码的设计原则之一是“显式优于隐式”。 使用 loc 和 iloc
可以让代码更容易维护, 可读性更高。 特别是在处理整数索引的对
象时。
- 第三种取值属性是 ix, 它是前两种索引器的混合形式, 在 Series
对象中 ix 等价于标准的 [ ](Python 列表) 取值方式。 ix 索引器
主要用于 DataFrame 对象,后面再具体介绍
DataFrame数据选择方法
DataFrame 在有些方面像二维或结构化数组, 在有些方面
又像一个共享索引的若干 Series 对象构成的字典。 这两种类比可以帮
助我们更好地掌握这种数据结构的数据选择方法。
- 将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 |
- 将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 |
- 其他取值方法
如果对单个标签取值
就选择列, 而对多个标签用切片就选择行:
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 会在
计算过程中对齐两个对象的索引。 当你处理不完整的数据时, 这一点非
常方便。
- 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
- 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 |
来源:CSDN
作者:少年吉
链接:https://blog.csdn.net/weixin_41503009/article/details/104185029