[Python3] Pandas v1.0 —— (一) 对象、数据取值与运算


[ Pandas version: 1.0.1 ]


import pandas as pd

一、Pandas对象

Pandas的三个基本数据结构:Series, DataFrame, Index

(一) Pandas的Series对象

Pandas的Series对象是一个带索引数据构成的一维数组。

通过values属性和index属性获取数据。values属性返回的结果与NumPy数组类似。

data = pd.Series([0.25, 0.5, 0.75, 1.0])
#0    0.25
#1    0.50
#2    0.75
#3    1.00
#dtype: float64
data.values 	#输出:array([0.25, 0.5 , 0.75, 1.  ])
data.index 		#输出:RangeIndex(start=0, stop=4, step=1)
data[1]
data[1:3]
#1    0.50
#2    0.75
#dtype: float64
  1. Series是通用的NumPy数组

NumPy数组通过隐式定义的整数索引获取数值。

Pandas的Series对象通过显式定义的索引与数值关联,其索引可以是任意类型,可使用不连续或不按顺序的索引。

data = pd.Series([0.25, 0.5, 0.75, 1.0], index=['a', 'b', 'c', 'd'])
data = pd.Series([0.25, 0.5, 0.75, 1.0], index=[2, 5, 3, 7])
  1. Series是特殊的字典

Python字典是一种将任意键映射到一组任意值的数据结构。

Series对象是一直将类型键映射到一组类型值的数据结构,支持字典不具有的功能,如切片。

population_dict = {'California': 38332521, 'Texas': 26448193, 'New York': 19651127}
population = pd.Series(population_dict)
# California    38332521
# Texas         26448193
# New York      19651127
# dtype: int64

population['California']
# 切片
population['California':'Texas']
  1. 创建Series对象:pd.Series(data, index=index)
  • 其中index是可选参数,data参数支持多种数据类型。
  • Series对象只会保留显式定义的键值对。
  • 每种形式都可以通过显示指定索引筛选需要的结果
# 1. data是列表或NumPy数组,这时index默认值为整数序列
pd.Series([2, 4, 6])

# 2. data是标量,创建Series对象时会重复填充到每个索引上
pd.Series(5, index=[100, 200, 300])

# 3. data是字典,index默认是排序的字典键
pd.Series({2:'a', 1:'b', 3:'c'})

# 每种形式都可以通过显示指定索引筛选需要的结果
pd.Series({2:'a', 1:'b', 3:'c'}, index=[3, 2])

#3    c
#2    a
#dtype: object

(二) Pandas的DataFrame对象

  1. DataFrame是通用的NumPy数组

DataFrame可以看做是一种有行和列索引的二维数组,也可看作是有序排列(有共同索引)的若干Series对象。

area_dict = {'California': 423967, 'Texas': 695662, 'New York': 141297}
area = pd.Series(area_dict)
states = pd.DataFrame({'population': population, 'area': area})

states
#             population    area
# California    38332521  423967
# Texas         26448193  695662
# New York      19651127  141297

DataFrame属性:indexcolumns

# DataFrame有index属性获取索引标签
states.index 	#输出:Index(['California', 'Texas', 'New York'], dtype='object')

# DataFrame有columns属性存放列标签的index对象
states.columns 	#输出:Index(['population', 'area'], dtype='object')
  1. DataFrame是特殊的字典:一列映射一个Series的数据
states['area']
# California    423967
# Texas         695662
# New York      141297
# Name: area, dtype: int64
  1. 创建DataFrame对象:pd.DataFrame(data, columns=columns, index=index)
# 1. 通过单个Series对象创建
pd.DataFrame(population, columns=['population'])
#             population
# California    38332521
# Texas         26448193
# New York      19651127


# 2. 通过字典列表创建
data = [{'a': i, 'b': 2 * i} for i in range(3)] 	#data: [{'a': 0, 'b': 0}, {'a': 1, 'b': 2}, {'a': 2, 'b': 4}]
pd.DataFrame(data)
#    a  b
# 0  0  0
# 1  1  2
# 2  2  4

# 即使字典中有些键不存在,Pandas也会用缺失值NaN表示
pd.DataFrame([{'a': 1, 'b': 2}, {'b': 3, 'c': 4}])
#      a  b    c
# 0  1.0  2  NaN
# 1  NaN  3  4.0

# 3. 通过Series对象字典创建
pd.DataFrame({'population': population, 'area': area})

# 4. 通过NumPy二维数组创建
pd.DataFrame(np.random.rand(3, 2), columns=['foo', 'bar'], index=['a', 'b', 'c'])
#         foo       bar
# a  0.428555  0.416076
# b  0.368652  0.115681
# c  0.381276  0.704019

# 注:如果不指定行列索引值,那么行列默认都是整数索引值
pd.DataFrame(np.random.rand(3, 2)
#           0         1
# 0  0.973628  0.960743
# 1  0.312451  0.824474
# 2  0.956125  0.275362

# 5. 通过NumPy结构化数组创建
A = np.zeros(3, dtype=[('A', 'i8'), ('B', 'f8')])
A 		#输出:array([(0, 0.), (0, 0.), (0, 0.)], dtype=[('A', '<i8'), ('B', '<f8')])
pd.DataFrame(A)
#    A    B
# 0  0  0.0
# 1  0  0.0
# 2  0  0.0

(三) Pandas的Index对象

  1. 将Index看作不可变数组

Index对象的索引是不可变的,这个不可变特性使得多个DataFrame和数组之间进行索引共享时更加安全,尤其是可以避免因修改索引时粗心导致的副作用。

ind = pd.Index([2, 3, 5, 7, 11])
ind 		#输出:Int64Index([2, 3, 5, 7, 11], dtype='int64')
ind[1] 		#输出:3
ind[::2] 	#输出:Int64Index([2, 5, 11], dtype='int64')
print(ind.size, ind.shape, ind.ndim, ind.dtype)
# 5 (5,) 1 int64

#ind[1] = 0 -> TypeError 不可赋值
  1. 将Index看作有序集合(实际上是一个多集:Index对象可以包含重复值)

Pandas对象涉及许多集合操作,遵循Python集合的许多习惯用法,包括并集、交集、差集等。

indA = pd.Index([1, 3, 5, 7, 9])
indB = pd.Index([2, 3, 5, 7, 11])
indA & indB 	#交集,输出:Int64Index([3, 5, 7], dtype='int64')
indA | indB 	#并集,输出:Int64Index([1, 2, 3, 5, 7, 9, 11], dtype='int64')
indA ^ indB 	#异或,输出:Int64Index([1, 2, 9, 11], dtype='int64')

二、数据取值与选择

(一) Series数据选择方法

1. 将Series看作字典

data = pd.Series([0.25, 0.5, 0.75, 1.0], index=['a', 'b', 'c', 'd'])
data['b']
# 使用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
data['e'] = 1.25

2. 将Series看作一维数组

# 数组数据选择功能:索引、花哨索引等

# 将显式索引作为切片
data['a': 'c']
# 将隐式索引作为切片
data[0:2]
# 掩码
data[(data > 3) & (data < 0.8)]
# 花哨索引
data[['a', 'e']]
  • 当使用显式索引作切片时,结果包含最后一个索引;而当使用隐式索引作切片时,结果不包含最后一个索引。

3. 索引器:loc, iloc, ix

  • 如果Series是显式整数索引,那么取值操作会使用显式索引,切片操作会使用隐式索引。(容易混淆)
data = pd.Series(['a', 'b', 'c'], index=[1, 3, 5])
# 1    a
# 3    b
# 5    c
# dtype: object

# 取值操作是显式索引
data[1] 		#输出:'a'

# 切片操作是隐式索引
data[1:3]
# 3    b
# 5    c
# dtype: object

第一种索引器:loc属性,表示取值和切片都是显式的

data.loc[1] 	#输出:'a'
data.loc[1:3] 	
# 1    a
# 3    b
# dtype: object

第二种索引器:iloc属性,表示取值和切片都是隐式索引(左闭右开区间)

data.iloc[1] 	#输出:'b'
data.iloc[1:3] 	
# 3    b
# 5    c
# dtype: object

第三种索引器:ix,它是lociloc混合形式,在Series对象中ix等价于标准Python列表取值方式。ix索引器主要用于DataFrame对象

Python代码的设计原则之一是“显式优于隐式”,使用lociloc可以让代码更易维护,可读性更高(尤其是在处理整数索引的对象时)

(二) DataFrame数据选择方法

1. 将DataFrame看作字典(若干Series对象构成)

states
#                    pop    area
# California    38332521  423967
# Texas         26448193  695662
# New York      19651127  141297

# 1. 通过对列名进行字典形式取值获取数据(dictionary-style)
states['area']
# 2. 用属性形式选择纯字符串列名的数据(attribute-style)
states.area
# 对同一个对象进行属性形式与字典形式的列数据结果相同
states.area is states['area'] 	#输出:True

# 如果列名不是纯字符串,或者列名与DataFrame的方法同名,就不能用属性索引
states.pop is states['pop'] 	#data.pop中,pop会显示为方法,无法获取列,输出:False

# 应该避免对用属性形式选择的列直接赋值
states['pop'] = z 		# ok
# states.pop = z 		# NO

# 可以用字典形式的语法调整对象:如,增加一列
states['density'] = states['pop'] / states['area']
#                    pop    area     density
# California    38332521  423967   90.413926
# Texas         26448193  695662   38.018740
# New York      19651127  141297  139.076746

2. 将DataFrame看作二维数组

# values属性按行查看数组数据
states.values
# array([[3.83325210e+07, 4.23967000e+05, 9.04139261e+01],
#        [2.64481930e+07, 6.95662000e+05, 3.80187404e+01],
#        [1.96511270e+07, 1.41297000e+05, 1.39076746e+02]])

# 行列转置
states.T
# 单个行索引获取一行数据
states.values[0] 		#输出:array([3.83325210e+07, 4.23967000e+05, 9.04139261e+01])
# 获取列数据
states['area']
# 索引器
states.iloc[:2, :1]
states.loc[:'Texas', :'area']
states.ix[:2, :'pop'] 	#注意:ix索引器对于整数索引的处理容易混淆
# 索引器结合掩码与花哨索引
states.loc[states.density > 100, ['pop', 'density']] 	
#                 pop     density
#New York    19651127  139.076746

# 任意取值方法都可以用于调整数据
states.iloc[0, 2] = 90

3. 其他取值方法

# 如果对单个标签取值就选择列,而对多个标签用切片就选择行
states['California':'Texas']
# 切片也可以不用索引值,直接用行数实现
states[:1]
# 掩码操作也可以直接对每一行进行过滤,而不需要使用loc索引器
states[states.density > 100]

三、Pandas数值运算方法

Pandas集成了NumPy的功能,除此之外,Pandas在保存数据内容与组合不同来源的数据具有优势。

高效技巧:对于一元运算(如函数与三角函数)的通用函数将在输出结果中保留索引和列标签;对于二元运算(如加法和乘法),在传递通用函数时会自动对齐索引进行计算。

(一) 通用函数:保留索引

rng = np.random.RandomState(42)
ser = pd.Series(rng.randint(0, 10, 4))
ser
# 0    6
# 1    3
# 2    7
# 3    4
# dtype: int64

# Pandas对象使用NumPy通用函数,结果依然保留索引
np.exp2(ser)
# 0     64.0
# 1      8.0
# 2    128.0
# 3     16.0
# dtype: float64

(二) 通用函数:索引对齐

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

  1. Series索引对齐
  • 整合两个数据源的数据,运算结果数组的索引时两个输入数组索引的并集。
  • 对于缺失位置的数据,Pandas会默认用NaN填充,表示此处无数。
  • 可以通过运算符方法的fill_value参数自定义缺失值
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

area.index | population.index
# Index(['Alaska', 'California', 'New York', 'Texas'], dtype='object')
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, fill_value=0)
# 0    2.0
# 1    5.0
# 2    9.0
# 3    5.0
# dtype: float64
  1. DataFrame索引对齐
rng = np.random.RandomState(42)
A = pd.DataFrame(rng.randint(0, 20, (2, 2)), columns=list('AB'))
#     A   B
# 0   6  19
# 1  14  10

B = pd.DataFrame(rng.randint(0, 10, (3, 3)), columns=list('BAC'))
#    B  A  C
# 0  7  4  6
# 1  9  2  6
# 2  7  4  3

# 两个对象的行列索引可以是不同顺序的,结果的索引会自动按顺序排列
A + B
#       A     B   C
# 0  10.0  26.0 NaN
# 1  16.0  19.0 NaN
# 2   NaN   NaN NaN

# 计算均值要用stack将二维数组压缩成一维数组
fill = A.stack().mean()
A.add(B, fill_value=fill)
#        A      B      C
# 0  10.00  26.00  18.25
# 1  16.00  19.00  18.25
# 2  16.25  19.25  15.25

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

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

(三) 通用函数:DataFrame与Series的运算

# 根据NumPy广播规则,让二维数组减自身的一行数据会按行计算。Pandas等同。
A = rng.randint(10, size=(3, 4))
A - A[0]

df = pd.DataFrame(A, columns=list('QRST'))
df - df.iloc[0]
# 按列计算,要用运算符方法,通过axis参数设置
df.subtract(df['R'], axis=0)

halfrow = df.iloc[0, ::2]
df - halfrow

Pandas相关阅读:

[Python3] Pandas v1.0 —— (一) 对象、数据取值与运算 【本文】
[Python3] Pandas v1.0 —— (二) 处理缺失值
[Python3] Pandas v1.0 —— (三) 层级索引
[Python3] Pandas v1.0 —— (四) 合并数据集
[Python3] Pandas v1.0 —— (五) 累计与分组
[Python3] Pandas v1.0 —— (六) 数据透视表
[Python3] Pandas v1.0 —— (七) 向量化字符串操作
[Python3] Pandas v1.0 —— (八) 处理时间序列
[Python3] Pandas v1.0 —— (九) 高性能Pandas: eval()与query()


总结自《Python数据科学手册》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章