chapter 1
基本import
1 | import numpy as np |
Chapter 2
Chapter 3: python
Chapter 4: numpy
创建ndarray
arange
1 | import pandas as pd |
1 |
|
创建函数
| 函数 | 描述 |
|---|---|
| array | 创造array |
| asarray | 转换成array |
| arange | 类似于range |
| ones | 全部为1的array,参数为shape |
| ones~like~ | 参数为array类型 |
| zeros | 类似于 ones |
| zeros~like~ | 类似于 ones~like~ |
| empty | 类似于 ones |
| empty~like~ | 类似于 ones~like~ |
| eye | 创建一个单位矩阵,参数为int |
| identity | like eye |
| meshgrid | 参数为两个一维数组,产生两个二维矩阵(对应两个数组中所有(x,y)对) |
a = np.identity(3)
print(a)
a = np.ones_like(a)
print(a)
1 | #+RESULTS: |
| 类型 | 缩写 | 说明 |
|---|---|---|
| int8 | i1 | 有符号8位int |
| uint8 | u1 | 无符号8位int |
| int16 | i2 | |
| uint16 | u2 | |
| int32 | i4 | |
| unit32 | u4 | |
| int64 | i8 | |
| uint64 | u8 | |
| float16 | f2 | 半精度浮点数 |
| float32 | f4 or f | 标准单精度浮点数,与C的float兼容 |
| float64 | f8 or d | 双精度,C的double和python的float兼容 |
| float128 | f16 or g | 扩展精度浮点数 |
| complex64 | c8 | 用两个32位浮点数表示的复数 |
| complex128 | c16 | |
| complex256 | c32 | |
| bool | ? | 储存True和False值的布尔类型 |
| object | O | python对象类型 |
| string_ | S | 固定长度的字符(每个字符1个字节)长度为10的字符串,应使用S10 |
| unicode_ | U | 固定长度的unicode |
数据类型转换astype
- 参数为数据类型
查看数据类型
- array的自带属性dtype
索引和切片
切片是原始数据的视图,而不是复制,修改会反映到原数组上 注意点</span> [切片是原始数据的视图而不是复制修改会反映到原数组上]
新的表示方法
1 | a = np.arange(15).reshape(3,5) |
1 | [[ 0 1 2 3 4] |
布尔型索引
否定符号 != / -
1
2
3
4
5
6
7
8
9
10
11names = np.array(["Bob","Joe", 'Will', 'Bob', "Will",'Joe','Joe'])
data = np.random.randn(7,4)
print(names)
print
print(data)
print
print(data[names == "Bob"])
print
print(data[names != "Bob"])
print
print(data[-(names == 'Bob')]) #numpy 新的表示方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25['Bob' 'Joe' 'Will' 'Bob' 'Will' 'Joe' 'Joe']
[[ -1.06602469e+00 1.70960938e+00 -5.37441602e-01 -1.26864579e+00]
[ 6.83392505e-01 -3.65908339e-01 -1.07189314e+00 1.78582204e+00]
[ -1.06516810e+00 1.74430728e+00 5.88376963e-01 1.59791560e-01]
[ -1.96586484e-01 1.14215100e-04 -7.00807814e-01 -1.75005941e-01]
[ 9.63746755e-01 -4.51637875e-01 -1.78610575e+00 6.75211511e-01]
[ -6.90968770e-01 5.80293483e-01 7.34267063e-01 4.58773330e-01]
[ -1.36539167e-01 1.90051999e+00 -1.13351041e+00 7.74997645e-01]]
[[ -1.06602469e+00 1.70960938e+00 -5.37441602e-01 -1.26864579e+00]
[ -1.96586484e-01 1.14215100e-04 -7.00807814e-01 -1.75005941e-01]]
[[ 0.6833925 -0.36590834 -1.07189314 1.78582204]
[-1.0651681 1.74430728 0.58837696 0.15979156]
[ 0.96374676 -0.45163788 -1.78610575 0.67521151]
[-0.69096877 0.58029348 0.73426706 0.45877333]
[-0.13653917 1.90051999 -1.13351041 0.77499765]]
[[ 0.6833925 -0.36590834 -1.07189314 1.78582204]
[-1.0651681 1.74430728 0.58837696 0.15979156]
[ 0.96374676 -0.45163788 -1.78610575 0.67521151]
[-0.69096877 0.58029348 0.73426706 0.45877333]
[-0.13653917 1.90051999 -1.13351041 0.77499765]]
/Users/Yueyec/.pyenv/versions/anaconda2-4.3.0/lib/python2.7/site-packages/ipykernel/__main__.py:11: DeprecationWarning: numpy boolean negative, the `-` operator, is deprecated, use the `~` operator or the logical_not function instead.
花式索引
是复制,不是视图
索引参数是一个list 里面是row index,可重复
参数是二阶list,得到的会是对应的单个元素 注意点</span>
1
2
3
4
5
6
7
8
9arr = np.arange(32).reshape((8,4))
print(arr)
print
print(arr[[1,5,7,2],[0,3,1,2]]) #会得到arr[1,0],arr[5,3],arr[7,1],arr[2,2]的一阶array
print
#第一种方法
print(arr[[1,5,7,2]][:,[0,3,1,2]])
print
print(arr[np.ix_([1,5,7,2],[0,3,1,2])]) #np.ix_将两个一维整数数组转换成一个用于区域的索引器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]
[12 13 14 15]
[16 17 18 19]
[20 21 22 23]
[24 25 26 27]
[28 29 30 31]]
[ 4 23 29 10]
[[ 4 7 5 6]
[20 23 21 22]
[28 31 29 30]
[ 8 11 9 10]]
[[ 4 7 5 6]
[20 23 21 22]
[28 31 29 30]
[ 8 11 9 10]]
数组转置和轴对换
转置为视图,非复制,但是不改变实例本身(self)的结构
T属性
- 轴颠倒
transpose()方法 没明白</span>
参数为由轴编号组成的元组
1
2
3
4arr = np.arange(16).reshape((2,2,4))
print(arr)
print "---------------------"
print(arr.transpose((1,0,2)))1
2
3
4
5
6
7
8
9
10
11[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]]
---------------------
[[[ 0 1 2 3]
[ 8 9 10 11]]
[[ 4 5 6 7]
[12 13 14 15]]]
swapaxes函数 没明白</span>
通用函数
常用一元函数列表
| 函数 | 描述 |
|---|---|
| abs | 绝对值 |
| fabs | 绝对值,对于非复数值,计算更快 |
| sqrt | 平方根,相当于arr ** 0.5 |
| square | 平方,相当于arr ** 2 |
| exp | 指数 e^x^ |
| log | 自然对数(底数为e) |
| log10 | 底数为10 |
| log2 | 底数为2 |
| log1p | log(1 +x) |
| sign | 各元素的正负号:1(正数),0(零),-1(负数) |
| ceil | ceiling值,即大于等于该数值的最小整数 |
| floor | floor值,即小于等于该数值的最大整数 |
| rint | 各元素四舍五入到最接近的整数,保留dtype |
| modf | 数组的小数和整数以两个独立数组的形式返回 |
| isnan | 返回一个表示“哪些值是NaN”的布尔型数组 |
| isfinite | 返回一个表示“那些值是有穷的(非inf,非NaN)”的布尔型数组 |
| isinf | 返回一个表示”哪些值是无穷”的布尔型数组 |
| cos | 三角函数 |
| cosh | 三角函数 |
| sin | 三角函数 |
| sinh | 三角函数 |
| tan | 三角函数 |
| tanh | 三角函数 |
| arccos | 反三角函数 |
| arccosh | 反三角函数 |
| arcsin | 反三角函数 |
| arcsinh | 反三角函数 |
| arctan | 反三角函数 |
| arctanh | 反三角函数 |
| logical~not~ | 计算各元素not x的真值,相当于-arr |
常用二元函数列表
| 函数 | 描述 |
|---|---|
| add | 元素相加 |
| subtract | 第一个数组减去第二个数组的元素 |
| multiply | 数组元素相乘 |
| divide | 除法 |
| floor~divide~ | 向下园整除法(丢弃余数) |
| power | 对于第一个数组元素A,第二个数组元素B,计算A** B |
| maximum | 计算最大值 |
| fmax | 计算最大值(忽略NaN) |
| fmin | 计算最小值(忽略NaN) |
| minimum | 计算最小值 |
| mod | 模运算 |
| copysign | 将第二数组中值的符号复制给第一个数组中的值 |
| greater | 相当于 > |
| greater~equal~ | 相当于 >= |
| less | 相当于 \< |
| less~equal~ | 相当于 \<= |
| equal | 相当于 == |
| not~equal~ | 相当于 != |
| logical~and~ | 相当于 & |
| logical~or~ | 相当于 |
| logical~xor~ | 相当于 \^ |
利用数组进行数据处理
假设在一组值(网格型)上计算函数sqrt(x^2^ + y^2^)
1 | a = np.arange(5) |
1 | [[0 1 2 3 4] |
1 | points = np.arange(-5,5,0.01) #1000个间隔相等的点 |
将条件逻辑表述为数组运算
numpy.where函数是三元表达式x if condition else y的矢量化版本
假设这三个变量
1
2
3
4import numpy as np
xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5])
yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5])
cond = np.array([True, False, True, True, False])假设根据cond的值来选取xarr与yarr的值,当cond为True,选取xarr,否者yarr
常规做法,对于大数组处理速度不快,无法作用于多维数组
1
2result = [x if c else y for x, y, c in zip(xarr, yarr, cond)]
print(result)1
[1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]
用where处理
1
2result = np.where(cond, xarr, yarr)
print(result)1
[ 1.1 2.2 1.3 1.4 2.5]
复杂的例子
常规的做法
1
2
3
4
5
6
7
8
9
10
11
12
13cond1 = cond
cond2 = np.array([True,False, False, True, True])
result = []
for i in range(len(cond1)):
if cond1[i] and cond2[i]:
result.append(0)
elif cond1[i]:
result.append(1)
elif cond2[i]:
result.append(2)
else:
result.append(3)
print(result)1
[0, 3, 1, 0, 2]
用where
1
2
3
4result = np.where(cond1 &cond2, 0,
np.where(cond1, 1,
np.where(cond2, 2, 3)))
print(result)1
[0 3 1 0 2]
用“布尔值在计算过程中可以被当做0或1处理”,用算术运算(感觉像黑魔法)
:有问题?:1
2result = 1* (cond1 - cond2) + 2* (cond2 & - cond1) +3 * - (cond1 | cond2)
print(result)1
[0 3 1 0 3]
数学和统计方法
sum, mean, std既可以当做实例方法,也可以当做顶级numpy函数使用
1 | arr = np.random.randn(5,4) #正态分布的数据 |
1 | 0.0748211510483 |
mean 与 sum这类函数可以接受一个axis参数(用于计算该轴上的统计值),最终结构是少一维度的数组 axis和想象中相反</span> [mean-与-sum这类函数可以接受一个axis参数用于计算该轴上的统计值最终结构是少一维度的数组]
1 | print arr |
1 | [[ 0.79860446 -0.67843272 2.58233563 0.33419308] |
cumsum 和 cumprod之类不聚合,产生一个由中间结果组成的数组
1 | import numpy as np |
1 | [[ 0 1 2] |
基本数组统计方法
| 方法 | 描述 |
|---|---|
| sum | 全部,或者某轴向的元素求和 |
| mean | 算术平均数,零长度的数组的mean为NaN |
| std | 标准差,自由度可调(默认为n) |
| var | 方差,自由度可调(默认为n) |
| min | 最小值 |
| max | 最大值 |
| argmin | 最小值索引 |
| argmax | 最大值索引 |
| cumsum | 元素的累计和 |
| cumprod | 元素的累计乘积 |
用于布尔型数组的方法
可以用sum来求True的计数
1
2arr = np.random.randn(100)
print((arr> 0).sum())1
52
any all 来测试数组是否存在一个及以上的True,是否所有值为True
1
2
3bools = np.array([False,False,True,False])
print(bools.any())
print(bools.all())1
2True
False
排序
numpy数组自带sort方法
1
2
3
4
5arr = np.random.randn(8)
print (arr)
print "---------------------------------------------------"
arr.sort()
print (arr)1
2
3
4
5[ 1.8328002 0.41074931 -0.60400869 1.53465964 -0.60704556 -0.16515945
-1.02657211 -0.91581907]
---------------------------------------------------
[-1.02657211 -0.91581907 -0.60704556 -0.60400869 -0.16515945 0.41074931
1.53465964 1.8328002 ]多维数组中任意轴上进行排序,只需参数线路轴编号
1
2
3
4
5arr = np.random.randn(5,3)
print arr
print "-----------------------------"
arr.sort(1)
print arr1
2
3
4
5
6
7
8
9
10
11[[-0.51009631 -1.66867772 -0.22805943]
[-0.66434219 0.38498122 -1.23044479]
[-0.39114451 0.01949044 -1.25717288]
[-0.00524041 0.42130187 -2.21689239]
[ 0.95358967 -0.94485412 -0.84467746]]
-----------------------------
[[-1.66867772 -0.51009631 -0.22805943]
[-1.23044479 -0.66434219 0.38498122]
[-1.25717288 -0.39114451 0.01949044]
[-2.21689239 -0.00524041 0.42130187]
[-0.94485412 -0.84467746 0.95358967]]顶级方法np.sort不会改变原数组,返回已经排序的副本
唯一化以及其他的集合逻辑
np.unique 返回数组已排序的唯一值
1
2
3
4
5
6names = np.array(["Bob", 'Joe','Will','Bob','Will','Joe','Joe'])
print np.unique(names)
ints = np.array([3,3,3,2,2,1,1,4,4])
print np.unique(ints)
print "-------------"
print sorted(set(names)) #纯python代码,与之等价1
2
3
4['Bob' 'Joe' 'Will']
[1 2 3 4]
-------------
['Bob', 'Joe', 'Will']np.in1d 返回一个数组的值是否在另一个数组中,返回一个布尔型数组
1
2
3
4values = np.array([6,0,0,3,2,5,6])
a = np.in1d(values, [2,3,6])
print a
print values[a]1
2[ True False False True True False True]
[6 3 2 6]数组集合运算
| 函数 | 描述 |
|—————————|———————————————————————————————|
| unique(x) | 计算x中的唯一元素,并返回有序的结果 |
| intersect1d(x,y) | 计算x和y的公共元素,并返回有序结果 |
| union1d(x,y) | 计算x和y的并集,并返回结果 |
| in1d(x,y) | 得到一个“x的元素是否包含于y”的布尔型数组 |
| setdiff1d(x,y) | 集合的差,机元素在x中且不在y中 |
| setxor1d(x,y) | 集合的对称差,即存在于一个数组中但不同是存在两个数组中的元素 |
| | |
用于数组的文件输入输出
以数组为二进制格式保存到磁盘
np.save, np.load
1
2
3arr = np.arange(10)
np.save('some_array',arr) #不加后缀名,自动添加.npy
print np.load("some_array.npy")1
[0 1 2 3 4 5 6 7 8 9]
np.savez
可以将doge数组保存到一个压缩文件中,将数组以关键字参数的形式传入1
2
3
4
5arr1 = np.arange(10)
arr2 = np.arange(1,20,2)
np.savez("array_archive.npz",a = arr1, b = arr2)
arch = np.load("array_archive.npz")
print arch["b"]
存取文本文件
np.loadtxt(“xx”, deliniter = “,”) #第二个参数为分隔符
np.genfromtxt() 面向的是结构化数组和缺失数据处理
线性代数
dot 方法
linalg
有一组标准的矩阵分解运算以及求逆和行列式 之类的东西
1
2
3
4
5
6
7
8
9
10
11
12
13
14import numpy as np
from numpy.linalg import inv, qr
from numpy.random import randn
x = randn(5,5)
mat = x.T.dot(x)
print "inv(mat)----------------"
print inv(mat)
print "mat.dot(inv(mat))---------------------------"
print mat.dot(inv(mat))
q, r = qr(mat)
print "q---------------------------"
print q
print "r---------------------------"
print r1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34inv(mat)----------------
[[ 5.24583468 14.98884503 9.71335736 -7.77651755 -22.8396013 ]
[ 14.98884503 46.52511417 28.81764808 -22.01122276 -69.70405132]
[ 9.71335736 28.81764808 18.67257302 -14.00870932 -44.03024424]
[ -7.77651755 -22.01122276 -14.00870932 12.70341866 32.9416411 ]
[ -22.8396013 -69.70405132 -44.03024424 32.9416411 106.1381638 ]]
mat.dot(inv(mat))---------------------------
[[ 1.00000000e+00 -1.27763158e-14 -3.16524540e-15 5.52810747e-15
-1.55455587e-14]
[ -8.09523246e-15 1.00000000e+00 -5.76325857e-15 -1.66274227e-15
2.52694539e-14]
[ 1.00097108e-15 2.71639602e-16 1.00000000e+00 -9.81680658e-15
3.54119545e-15]
[ -6.64139148e-16 1.57971687e-14 3.29027055e-15 1.00000000e+00
-2.99822967e-14]
[ -3.04200410e-15 -8.84058697e-15 1.92099465e-15 -8.57631485e-15
1.00000000e+00]]
q---------------------------
[[-0.91326408 0.01491589 -0.30364189 0.21681123 -0.16285184]
[-0.08041303 -0.74311076 0.43827531 -0.04710955 -0.49700662]
[ 0.33046062 -0.31708316 -0.83157252 0.01337763 -0.31394621]
[-0.21986762 -0.15288857 -0.13263187 -0.92494467 0.23488181]
[-0.04400528 -0.56890005 -0.08131595 0.30833745 0.75679059]]
r---------------------------
[[ -1.40092662e+01 -1.26845899e+00 5.84338504e+00 -3.35403461e+00
-3.83026371e-01]
[ 0.00000000e+00 -2.13935952e+00 -2.42468320e+00 -6.51800989e-01
-2.21389665e+00]
[ 0.00000000e+00 0.00000000e+00 -2.44765043e+00 -3.82142992e-01
-8.97542800e-01]
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 -4.11638083e-01
1.30663382e-01]
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
7.13024012e-03]]linalg的一些常用函数
| 函数 | 描述 |
|———-|————————————————————————————————————————————————-|
| diag | 以一维数组的形式返回方阵的对角线(或非对角线)元素,或将一维数组转换成为方阵(非对角线元素为0) |
| dot | 矩阵乘法 |
| trace | 计算对角线元素的和 |
| det | 计算矩阵行列式 |
| eig | 计算方阵的本征值和本征向量 |
| inv | 计算方阵的逆 |
| pinv | 计算矩阵的Moore-Penrose违逆 |
| qr | 计算QR分解 |
| svd | 计算奇异值分解(SVD) |
| solve | 解线性方程Ax= b,其中A为一个方阵 |
| lstsq | 计算Ax = b的最小二乘解 |
随机数生成
numpy.random模块对内置random进行了补充
1
2
3samples = np.random.normal(size = (4,4))
print samples1
2
3
4[[-0.2577954 -1.84871926 -1.73680389 0.75279352]
[-0.39841909 -1.57836749 0.58627939 -0.02390301]
[ 0.26546489 1.05297095 0.19336693 0.90791299]
[-0.74260739 -1.06363081 0.27326372 -0.00851723]]部分numpy.random函数
| 函数 | 描述 |
|——————-|————————————————————————————————|
| seed | 确定随机数生成器的种子 |
| permutation | 返回一个序列的随机排列或返回一个随机排列的范围 |
| shuffle | 对一个序列就地随机排列 |
| rand | 产生均匀分布的样本值 |
| randint | 从给定的上下限范围内随机选取整数,size参数(int) |
| randn | 产生正态分布(平均值为0,标准差为1)的样本值,类似于MATLAB接口 |
| binomial | 产生二项分布的样本值 |
| normal | 产生正态(高斯)分布的样本值 |
| beta | 产生Beta分布的样本值 |
| chisquare | 产生卡方分布的样本值 |
| gamma | 产生Gamma分布的样本值 |
| uniform | 产生在[0,1)中均匀分布的样本值 |
| | |范例:随机漫步
单个随机漫步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import numpy as np
import random
#传统python代码
position = 0
walk = [position]
steps = 1000
for i in xrange(steps):
step = 1 if random.randint(0,1) else -1 #新的写法
position += step
walk.append(position)
#numpy.random方法
nsteps = 1000
draws = np.random.randint(0,2,size = nsteps)
steps = np.where(draws >0, 1,-1)
walk = steps.cumsum()
print "walk.min()----------"
print walk.min()
print "walk.max()------------"
print walk.max()1
2
3
4walk.min()----------
-14
walk.max()------------
26一次性模拟多个随机漫步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20nwalks = 5000
nsteps = 1000
draws = np.random.randint(0,2, size = (nwalks, nsteps))
steps = np.where(draws >0, 1, -1)
walks = steps.cumsum(1)
print "walks.max()----------"
print walks.max()
print "walks.min()----------"
print walks.min()
#选出超过30或-30的walk
hits30 = (np.abs(walks) >= 30).any(1) #any是在轴1上广播,即计算各个行中有没有绝对值30以上的
print "到达30或-30的数量"
print hits30.sum() #到达30或-30的数量
#利用这个数据来选出那些行,并调用argmax在轴1上获取穿越时间
crossing_times = (np.abs(walks[hits30]) >= 30).argmax(1)
print "crossing_times.mean()-------"
print crossing_times.mean()
#其他分布方式得到漫步数据
steps = np.random.normal(loc = 0, scale = 0.25, size = (nwalks,nsteps))1
2
3
4
5
6
7
8walks.max()----------
119
walks.min()----------
-107
到达30或-30的数量
3329
crossing_times.mean()-------
497.677080204
Chapter 5: pandas入门
数据结构
Series
类似于一维数组的对象,由一组数组和一组与之对应的数据标签(即索引)组成
1
2
3import pandas as pd
obj = pd.Series([4,7,-5,3])
print obj1
2
3
4
50 4
1 7
2 -5
3 3
dtype: int64values 和 index属性
1
2print obj.values
print obj.index1
2[ 4 7 -5 3]
RangeIndex(start=0, stop=4, step=1)自定义index
1
2
3obj2 = pd.Series([4,7,-5,3], index = ['d', 'b', 'a', 'c'])
print obj2
print obj2.index1
2
3
4
5
6d 4
b 7
a -5
c 3
dtype: int64
Index([u'd', u'b', u'a', u'c'], dtype='object')通过索引来选取Series中的值
1
2
3
4print obj2['a']
obj2['d'] = 6
print obj2[['c','a','d']]1
2
3
4
5-5
c 3
a -5
d 6
dtype: int64**
1
2
3print obj2[obj2 > 0]
print obj2 * 2
print np.exp(obj2)1
2
3
4
5
6
7
8
9
10
11
12
13
14d 6
b 7
c 3
dtype: int64
d 12
b 14
a -10
c 6
dtype: int64
d 403.428793
b 1096.633158
a 0.006738
c 20.085537
dtype: float64通过字典直接建立Series
1
2
3sdata = {"Ohio" : 35000, 'Texas': 71000, "Oregon": 16000, "Utah": 5000}
obj3 = pd.Series(sdata)
print obj31
2
3
4
5Ohio 35000
Oregon 16000
Texas 71000
Utah 5000
dtype: int64传入字典,并有index参数,则用index来排序,从字典中寻找index对应的值,没有的话,NaN来代替
1
2
3states = ['California', "Ohio", 'Oregon','Texas']
obj4 = pd.Series(sdata,index = states)
print obj41
2
3
4
5California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64pd.isnull, pd.notnull 寻找缺失数据
1
2
3print pd.isnull(obj4)
print
print pd.notnull(obj4)1
2
3
4
5
6
7
8
9
10
11California True
Ohio False
Oregon False
Texas False
dtype: bool
California False
Ohio True
Oregon True
Texas True
dtype: boolSeries 算术运算中会自动对齐不同的数据 注意点</span>
1
2
3
4
5print obj3
print
print obj4
print
print obj3 + obj41
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18Ohio 35000
Oregon 16000
Texas 71000
Utah 5000
dtype: int64
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64
California NaN
Ohio 70000.0
Oregon 32000.0
Texas 142000.0
Utah NaN
dtype: float64Series 本身 与 index(索引) 有name属性
1
2
3obj4.name = 'population'
obj4.index.name = "state"
print obj41
2
3
4
5
6state
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
Name: population, dtype: float64修改 Series的索引
1
2
3
4print obj
print
obj.index = ['Bob', "Steve", 'Jeff', "Ryan"]
print obj1
2
3
4
5
6
7
8
9
10
110 4
1 7
2 -5
3 3
dtype: int64
Bob 4
Steve 7
Jeff -5
Ryan 3
dtype: int64
DataFrame
创建DataFrame
简单例子
1
2
3
4
5
6
7
8import numpy as np
import pandas as pd
data = {"state": ['Ohio', 'Ohio', 'Ohio','Nevada','Nevada'],
'Year':[2000,2001,2002,2001,2002],
"pop":[1.5,1.7,3.6, 2.4, 2.9]}
frame = pd.DataFrame(data)
print frame1
2
3
4
5
6Year pop state
0 2000 1.5 Ohio
1 2001 1.7 Ohio
2 2002 3.6 Ohio
3 2001 2.4 Nevada
4 2002 2.9 Nevada指定列序号,排序列顺序
1
2
3d1 = pd.DataFrame(data,columns = ['year','state','pop'])
print d11
2
3
4
5
6year state pop
0 NaN Ohio 1.5
1 NaN Ohio 1.7
2 NaN Ohio 3.6
3 NaN Nevada 2.4
4 NaN Nevada 2.9传入的列在数据中找不到时,会产生NA值
1
2
3
4frame2 = pd.DataFrame(data,columns = ["year",'state','pop','debt'],
index = ['one','two','three','four','five'])
print frame2
print frame2.columns1
2
3
4
5
6
7year state pop debt
one NaN Ohio 1.5 NaN
two NaN Ohio 1.7 NaN
three NaN Ohio 3.6 NaN
four NaN Nevada 2.4 NaN
five NaN Nevada 2.9 NaN
Index([u'year', u'state', u'pop', u'debt'], dtype='object')用字典或者属性的方式来获得DataFrame的列(Series)
1
2
3print frame2['state']
print
print frame2.year1
2
3
4
5
6
7
8
9
10
11
12
13one Ohio
two Ohio
three Ohio
four Nevada
five Nevada
Name: state, dtype: object
one NaN
two NaN
three NaN
four NaN
five NaN
Name: year, dtype: object获得DataFrame的行
1
print frame2.ix['three']
1
2
3
4
5year NaN
state Ohio
pop 3.6
debe NaN
Name: three, dtype: object修改列数值
1
2
3
4
5frame2.debt = 16.5
print frame2
print
frame2['debt'] = np.arange(5.)
print frame21
2
3
4
5
6
7
8
9
10
11
12
13year state pop debt
one NaN Ohio 1.5 16.5
two NaN Ohio 1.7 16.5
three NaN Ohio 3.6 16.5
four NaN Nevada 2.4 16.5
five NaN Nevada 2.9 16.5
year state pop debt
one NaN Ohio 1.5 0.0
two NaN Ohio 1.7 1.0
three NaN Ohio 3.6 2.0
four NaN Nevada 2.4 3.0
five NaN Nevada 2.9 4.0将列表或者数组赋值给列
空位会补上缺失值
1
2
3
4
5
6
7
8val = pd.Series([-1.2, -1.5, -1.7], index = ['two','four','five'])
frame2.debt = val
print frame2
print
# index没有的,不会赋值上去
val = pd.Series([-1.2, -1.5, -1.7], index = ['two','four','six'])
frame2.debt = val
print frame21
2
3
4
5
6
7
8
9
10
11
12
13year state pop debt
one NaN Ohio 1.5 NaN
two NaN Ohio 1.7 -1.2
three NaN Ohio 3.6 NaN
four NaN Nevada 2.4 -1.5
five NaN Nevada 2.9 -1.7
year state pop debt
one NaN Ohio 1.5 NaN
two NaN Ohio 1.7 -1.2
three NaN Ohio 3.6 NaN
four NaN Nevada 2.4 -1.5
five NaN Nevada 2.9 NaN不存在列赋值会创造新的列,可用del关键字删除列
1
2
3
4
5frame2.eastern = frame2.state == "Ohio"
print frame2
del frame2.eastern
print frame2.columns1
2
3
4
5
6
7year state pop debt
one NaN Ohio 1.5 NaN
two NaN Ohio 1.7 -1.2
three NaN Ohio 3.6 NaN
four NaN Nevada 2.4 -1.5
five NaN Nevada 2.9 NaN
Index([u'year', u'state', u'pop', u'debt'], dtype='object')
嵌套字典
1
2
3
4pop = {'Nevada': {2001: 2.4, 2002: 2.9},
"Ohio": {2000:1.5, 2001:1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop)
print frame31
2
3
4Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6转置
1
print frame3.T
1
2
32000 2001 2002
Nevada NaN 2.4 2.9
Ohio 1.5 1.7 3.6
内层字典的键会被合并,排序以形成最终的索引,如果现实指定索引,则不会
1
print pd.DataFrame(pop, index = [2002,2003,2001])
1
2
3
4Nevada Ohio
2002 2.9 3.6
2003 NaN NaN
2001 2.4 1.7由Series组成的字典也是一样用法
1
2
3
4
5pdata = {'Ohio': frame3['Ohio'][:-1],
'Nevada': frame3['Nevada'][:2]}
print pdata
print
print pd.DataFrame(pdata)1
2
3
4
5{'Ohio': 2000 1.5
2001 1.7
Name: Ohio, dtype: float64, 'Nevada': 2000 NaN
2001 2.4
Name: Nevada, dtype: float64}:
1
2
3Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7可以输入给DataFrame的数据
| 类型 | 描述 |
|———————————————|—————————————————————————————————————|
| 二维ndarray | 数据矩阵,还可以传入行标和列标 |
| 由数组、列表或元组组成的字典 | 每个序列会变成DF的一列,所有序列的长度必须相同 |
| NumPy的结构化/记录数组 | 类似于“有数组组成的字典” |
| 由series组成的字典 | 每一个Serire会变成一列。如没遇显示指定索引,各索引会被整合成结果的行索引 |
| 由字典组成的字典 | 各内层字典会成一列。键会被合并成结果的行索引 |
| 字典或Series的列表 | 各项会成DF的一行,字典键或Series索引的并集将成为DF的列表 |
| 由列表或元组组成的列表 | 类似于“二维ndarray” |
| 另一个DF | 该DF的索引将会被沿用,除非显示指定其他索引 |
| NumPy的MaskedArray | 类似于“二维ndarray”,只是掩码值在结果的DF将会变成NA/缺失值 |
| | |如果设置了index和columns的name属性,这些信息也会被显示出来
1
2
3frame3.index.name = "year"
frame3.columns.name = "state"
print frame31
2
3
4
5state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6values属性会以二维ndarray形式返回DF的数组
1
print frame3.values
1
2
3[[ nan 1.5]
[ 2.4 1.7]
[ 2.9 3.6]]如果DF各列数组类型不同,则会选取能兼容所有列的数据类型
索引对象
负责管理轴标签和其他元数据,构建Series或DF时,所用到的任何数组或其他序列的标签会转换成index
1
2
3
4obj = pd.Series(range(3), index = ['a','b','c'])
index = obj.index
print index
print index[1:]1
2Index([u'a', u'b', u'c'], dtype='object')
Index([u'b', u'c'], dtype='object')index对象不可修改 注意点</span>
主要的index对象
| 类 | 说明 |
|———————-|——————————————————————————————————|
| index | 最泛化的index对象,将轴标签标示为一个由python对象组成的NumPy数组 |
| int64index | 针对整数的特殊index |
| MultiIndex | “层次化”索引对象,标示单个轴上的多层索引,可以看做由元组组成的数组 |
| DatetimeIndex | 存储纳秒级时间戳(用NumPy的datetime64类型标示) |
| PeriodIndex | 针对Period数据(时间间隔)的特殊Index |
| | |index的方法和属性
| 方法 | 说明 |
|———————-|——————————————————————————|
| append | 连接另一个index对象,产生一个新的index |
| diff | 计算差集,得到一个index |
| intersection | 计算交集 |
| union | 计算并集 |
| isin | 计算一个指示各值是否都包含在参数集合中的布尔型数组 |
| delete | 删除索引i处的元素,并得到一个新的index |
| drop | 删除传入的值,并得到新的index |
| insert | 将元素插入到索引i出,并得到新的index |
| is~monotonic~ | 当各元素均大于前一个元素时,返回True |
| is-unique | 当index没有重复值时,返回True |
| unique | 计算index中的唯一值的数组 |
| | |
基本功能
重新索引 reindex
从新排序,没有则缺失值,可以用fill~value参数填充缺失值~
1
2
3
4
5
6
7
8obj = pd.Series([4.5, 7.2, -5.3, 3.6], index = ['d','b', 'a', 'c'])
print obj
#调用reindex将会根据新索引进行重拍,如果索引值不存在,引入缺失值
obj2 = obj.reindex(['a','b','c','d','e'])
print obj2
temp = obj.reindex(['a','b','c','d','e'],fill_value = 0) #调补缺失值
print temp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
a -5.3
b 7.2
c 3.6
d 4.5
e 0.0
dtype: float64填充
1
2obj3= pd.Series(['blue','purpel','yellow'],index = [0,2,4])
print obj3.reindex(range(6), method = 'ffill') #向前填充1
2
3
4
5
6
70 blue
1 blue
2 purpel
3 purpel
4 yellow
5 yellow
dtype: objectmethod选项
| 参数 | 说明 |
|————————|———————————|
| ffill或pad | 前向填充(或搬运)值 |
| bfill,backfill | 后向填充(或搬运)值 |
| | |reindex可以修改(行)索引、列或者两者都改,如果仅传入一个序列,则会重新索引行
注意点</span>1
2
3
4
5
6
7
8
9
10
11
12
13
14frame = pd.DataFrame(np.arange(9).reshape((3,3)),index = ['a', 'c', 'd'],
columns =['Ohio', 'Texas', "California"])
print frame
print
frame2 = frame.reindex(['a', 'b', 'c', 'd'])
print frame2
#使用columns关键字可以重新索引列
states = ['Texas', 'Utah', "California"]
print frame.reindex(columns = states)
#同时对行和列重新索引,而插值则只能按行应用(即轴0);各行间进行向上补充
print frame.reindex(index = ['a','b', 'c', 'd'],method = 'ffill', columns = states)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
Ohio Texas California
a 0.0 1.0 2.0
b NaN NaN NaN
c 3.0 4.0 5.0
d 6.0 7.0 8.0
Texas Utah California
a 1 NaN 2
c 4 NaN 5
d 7 NaN 8
Texas Utah California
a 1 NaN 2
b 1 NaN 2
c 4 NaN 5
d 7 NaN 8利用ix的标签索引功能,重新索引任务可以变得更简洁 :ix 不懂:
1
print frame.ix[['a','b','c','d'], states]
1
2
3
4
5Texas Utah California
a 1.0 NaN 2.0
b NaN NaN NaN
c 4.0 NaN 5.0
d 7.0 NaN 8.0reindex函数的参数
| 参数 | 说明 |
|——————-|—————————————————————————————————-|
| index | 用作索引的新序列。既可以是index的实例,也可以是其他序列的数据结构 |
| method | 插值(填充)方式 |
| fill~value~ | 在重新索引过程中,引入缺失值的代替值 |
| limit | 向前或向后填充时的最大填充量 |
| level | 在MultiIndex的指定级别上匹配简单索引,否则选取其子集 |
| copy | 默认为True,如果为False,则新旧相等就不复制 |
| | |
丢弃指定轴上的项
drap 方法返回删除了的新对象
1
2
3
4
5
6
7obj = pd.Series(np.arange(5.),index = ['a','b', 'c', 'd', 'e'])
new_obj = obj.drop("c")
print new_obj
print
print obj
print
print obj.drop(['d','c'])1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
a 0.0
b 1.0
c 2.0
d 3.0
e 4.0
dtype: float64
a 0.0
b 1.0
e 4.0
dtype: float64产出随意轴上的索引值
1
2
3
4
5data = pd.DataFrame(np.arange(16).reshape(4,4), index = ['Ohio','Colorado', "Utah", 'New York'], columns = ['one', 'two', 'three', 'four'])
print data.drop(['Colorado', 'Ohio'])
print
print data.drop('two', axis = 1)
print1
2
3
4
5
6
7
8
9one two three four
Utah 8 9 10 11
New York 12 13 14 15
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15
索引,选取和过滤
索引可以不是整数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15obj = pd.Series(np.arange(4),index = ['a','b','c','d'])
print obj['b']
print
print obj[1]
print
print obj[2:4]
print
print obj[['b','a','d']]
print
print obj[[1,3]]
print
print obj[obj < 2] #注意点,不是bool
temp = np.arange(4)
print
print temp[temp < 2] #注意点1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
221
1
c 2
d 3
dtype: int64
b 1
a 0
d 3
dtype: int64
b 1
d 3
dtype: int64
a 0
b 1
dtype: int64
[0 1]用标签的切片,末端是包含的 注意点</span>
1
2
3
4
5print obj['b':'c']
#修改数值
obj["b":'c'] = 5
print obj1
2
3
4
5
6
7
8b 1
c 2
dtype: int32
a 0
b 5
c 5
d 3
dtype: int32DF的索引其实是获取一个或多个列 注意点</span>
1
2
3
4
5
6
7
8
9
10
11
12
13data = pd.DataFrame(np.arange(16).reshape(4,4),
index= ['Ohio','Colorado','Utah','New York'],
columns = ['one', 'two','three','four'])
print data
print
print data['two']
print data[['three','one']]
#切片来索引
print data[:2]
#bool数组来选取
print data[data["three"] > 5]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
Ohio 1
Colorado 5
Utah 9
New York 13
Name: two, dtype: int32
three one
Ohio 2 0
Colorado 6 4
Utah 10 8
New York 14 12
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
one two three four
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15ix 标签索引(行)
1
2
3
4
5
6
7print data.ix["Colorado", ["two", "three"]]
print
print data.ix[["colorado", "utah"], [3,0,1]]
print
print data.ix[2]
print
print data.ix[data.three >5, 3]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18two 5
three 6
Name: Colorado, dtype: int32
four one two
colorado NaN NaN NaN
utah NaN NaN NaN
one 8
two 9
three 10
four 11
Name: Utah, dtype: int32
Colorado 7
Utah 11
New York 15
Name: four, dtype: int32
DF索引说明
| 类型 | 说明 |
|—————————————-|——————————————————————|
| obj[val] | 选取DF的单列或者一组列 |
| obj.ix[val] | 选取DF的单个行或者一组行 |
| obj.ix[:,val] | 选取单个列或列子集 |
| obj.ix[val1,val2] | 同时选取行和列 |
| reindex方法 | 将一个或多个轴匹配或重新索引 |
| xs方法 | 港剧标签选取单行或单列,返回一个Series |
| icol,irow方法 | 根据整数未知选取单列或单行,返回一个Series |
| get~value~,set~value方法~ | 根据行或列标签选取单个值 |
| | |
算术运算和数据对齐
如果存在不同的索引对,则结果是并集,缺失则为NA值
在算术方法中填填充
1
2
3
4
5
6
7
8
9
10df1 = pd.DataFrame(np.arange(12.).reshape((3,4)),columns = list('abcd'))
df2 = pd.DataFrame(np.arange(20.).reshape((4,5)),columns = list('abcde'))
print "df1"
print df1
print "df2"
print df2
print df1 + df2
print
#填写缺失值
print df1.add(df2,fill_value = 0)部分算术方法
| 方法 | 说明 |
|———|———|
| add | + |
| sub | - |
| div | / |
| mul | * |DF和Series之间的运算(会广播)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20arr = np.arange(12).reshape(3,4)
print "arr"
print arr
print arr - arr[0]
#DF和Serise之间
frame = pd.DataFrame(np.arange(12).reshape(4,3),
columns = list('abc'),index = ['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.ix[0]
series1 = frame['a']
print "DF:frame"
print frame
print "series"
print series
print frame - series
print
print series1
print
# 列相减不成功
print frame - series1用运算方法来处理匹配列的运算
1
2#继续上面
print frame.sub(series1,axis = 0)
函数应用和映射
Numpy的ufuncs(元素级数组方法)可用于操作pandas对象
1
2
3
4
5
6
7
8
9
10frame = pd.DataFrame(np.random.randn(4,3),columns = list('bde'),
index = ['Utah', 'Ohio', 'Texas', 'Oregon'])
print frame
print np.abs(frame)
# 使用DF的apply方法来实现函数功能
f = lambda x: x.max() - x.min()
print frame.apply(f)
print
print frame.apply(f, axis = 1)改变格式(格式化字符串) applymap
1
2format = lambda x: '%.2f' % x
print frame.applymap(format)单列运算
1
print frame['e'].map(format)
排序和排名
排序
sort~index~
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#sort the series
obj = pd.Series(range(4), index = list('dabc'))
print obj.sort_index()
#sort the DF
frame = pd.DataFrame(np.arange(8).reshape(2,4),
index = ['three', 'one'],
columns = list('dabc'))
#sort the frame by the index unkonwn
print frame.sort_index()
print
#sort hte frame by the columns unkonwn
print frame.sort_index(axis = 1)
print
# 降序
print frame.sort_index(axis = 1, ascending = False)
print
#series排序,可以用 order方法
obj = pd.Series([4,7,3,2])
print obj.order()
print
#NA值会排在最后面
obj = pd.Series([4, np.nan, 7, np.nan, -3 ,2])
print obj.order()
print根据列或者多个列排序
1
2
3
4
5
6
7
8
9frame = pd.DataFrame({'b': [4,7,-3,2], 'a': [0, 1, 0, 1]})
print frame
print
#sort by column b'values
print frame.sort_index(by = 'b')
#sort by column a's values ,then sort by b's values
print frame.sort_index(by = ['a', 'b'])
排名ranking 没明白</span>
排名和排序关系密切,且他会增设一个排名值(从1开始,直到有效数据的数量)
和numpy.argsort产生的简洁排序索引类似,不过可以根据规则破坏平级关系
1
2
3
4
5obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
obj1 = pd.Series(np.arange(4))
print obj.rank()
print
print obj1.rank()根据出现顺序来给出排名(类似于稳定排序)
1
print obj.rank(method = 'first')
降序
1
print obj.rank(ascending = False, method = 'max')
用于破坏平级关系的method选项
| 方法 | 说明 |
|—————-|—————————————————————|
| ‘average’ | 默认:在相等分组中,为各个值分配平均排名 |
| “min” | 使用整个分组的最小排名 |
| “max” | 使用整个分组的最大排名 |
| “first” | 安值在原始数据的出现顺序分配排名 |
带有重复值的轴索引
虽然很多pandas函数要求轴标签唯一,但是不是强制性的
1
2obj = pd.Series(range(5), index = list('aabbc'))
print objis~unique属性可以测试值是否唯一~
1
print obj.index.is_unique
对应多个值,会返回一个Serise,单个值则返回标准值
1
2
3
4
5#有两个对应的值
print obj['a']
print
#单个对应的值
print obj['c']:
1
4
DF索引的情况也如此
1
2
3df = pd.DataFrame(np.random.randn(4,3),index = list('aabb'))
print df
print df.ix['b']
汇总和计算描述统计
这组数学和统计方法基于没有缺失数据的假设而构建的
1
2
3
4
5df = pd.DataFrame([[1.4, np.nan],[7.1, -4.5],
[np.nan, np.nan], [0.75, -1.3]],
index = list('abcd'),
columns = ['one', "two"])
print df1
2
3
4
5one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3会自动跳过NA值,除非整个切片(行或列)都是NA,可通过skipna选项禁用这功能
1
2
3
4
5
6
7
8
9
10#跳过NA值,计算
print df.sum()
print
#对行进行计算
print df.sum(axis = 1)
print
#禁用skipna
print df.mean(axis = 1, skipna = False)约简方法的选项
| 选项 | 说明 |
|————|——————————————————————————-|
| aixs | 约简的轴。DataFrame的行用0,列用1 |
| skipna | 排除缺失值,默认值为True |
| level | 如果轴是层次化索引的(MultiIndex),根据level分组约简 |
有些是间接统计(如返回最大值或最小值索引)
1
2
3
4
5
6#返回列中最大值的索引
print df.idxmax()
print
#累计求和
print df.cumsum()1
2
3one b
two d
dtype: object:
1
2
3
4
5one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8既不是约简型也不是累计型
describe 一次性产品多个汇总统计
1
2
3
4
5
6
7
8
9#数字型的汇总统计
print df
print
print df.describe()
print
#非数字的汇总统计
obj = pd.Series(list('aabc') * 4)
print obj.describe()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
one two
count 3.000000 2.000000
mean 3.083333 -2.900000
std 3.493685 2.262742
min 0.750000 -4.500000
25% 1.075000 -3.700000
50% 1.400000 -2.900000
75% 4.250000 -2.100000
max 7.100000 -1.300000
count 16
unique 3
top a
freq 8
dtype: object
描述和汇总统计
| 方法 | 说明 |
|———————-|————————————————————|
| count | 非NA值的数量 |
| describe | 针对Series或各DF列计算汇总统计 |
| min max | 计算最小值和最大值 |
| argmin argmax | 计算能获得最小或最大的索引位置(整数) |
| idxmin idxmax | 计算能获得最小或最大的索引值 |
| quantile | 计算样本的分位数(0到1) |
| sum | 值的总和 |
| mean | 值的平均值 |
| median | 值的算术中位数 |
| mad | 根据平均值计算平均绝对离差 |
| var | 样本值的方差 |
| std | 样本值的标准差 |
| skew | 样本值的偏度(三阶矩) |
| kurt | 样本值的峰度(四阶距) |
| cumsum | 样本值的累计和 |
| cummin cummax | 样本指的累计最大值和最小值 |
| cumprod | 累计值的累计积 |
| diff | 计算一阶差分(队时间序列很有用0 |
| pct~change~ | 计算百分数变化 |
相关系数和协方差
通过参数对计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14import pandas.io.data as web
#获取来自Yahoo!Finance的股票价格和成交量
all_data = {}
for ticker in ['AAPL', 'IBM', 'MSFT']:
all_data[ticker] = web.get_data_yahoo(ticker,'1/1/2000','1/1/2000')
price = pd.DataFrame({tic: data['Adj Close']
for tic, data in all_data.iteritems()})
volume = pd.DataFrame({tic : data['Volume']
for tic, data in all_data.iteritems()})
#计算价格的百分数变化
returns = price.pct_change()
print returns.tail()1
2
3Empty DataFrame
Columns: [AAPL, IBM, MSFT]
Index: []
唯一值 值计算以及成员资格
unique方法,返回obj的唯一值数组
1
2
3
4
5
6obj = pd.Series(list('cadaabbcc'))
#得到唯一值
uniques = obj.unique()
print uniques
print1
['c' 'a' 'd' 'b']
:
value~counts~ 计算一个Series中各值出现的频率
1
2
3
4
5
6#序列是降序排列的
print obj.value_counts()
print
#除了方法还有pd的顶级方法
print pd.value_counts(obj.values, sort = False)1
2
3
4
5
6
7
8
9
10
11c 3
a 3
b 2
d 1
dtype: int64
a 3
c 3
b 2
d 1
dtype: int64isin 判断矢量化集合的成员资格
1
2
3
4mask = obj.isin(['b', 'c'])
print mask
print
print obj[mask]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
170 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
0 c
5 b
6 b
7 c
8 c
dtype: object唯一值 值计算 成员资格方法
| 方法 | 说明 |
|———————-|———————————————————————————————|
| isin | 计算一个表示“Series各值是否包含于传入的值序列中”的布尔型数组 |
| unique | 计算Series中的唯一值数组,安发现的顺序返回 |
| value~counts~ | 返回一个Series,其索引为唯一值,其值为频率,降序排序 |得到DF多个相关列柱状图
1
2
3
4
5
6
7
8
9data = pd.DataFrame({'Qu1': [1,3,4,3,4],
'Qu2': [2,3,1,2,3],
"QU3": [1,5,2,4,4]})
print data
print
#将pandas.value_counts传给DF的apply函数得到各列的元素个数
#NA值填充为0
result = data.apply(pd.value_counts).fillna(0)
print result1
2
3
4
5
6
7
8
9
10
11
12
13QU3 Qu1 Qu2
0 1 1 2
1 5 3 3
2 2 4 1
3 4 3 2
4 4 4 3
QU3 Qu1 Qu2
1 1.0 1.0 1.0
2 1.0 0.0 2.0
3 0.0 2.0 2.0
4 2.0 2.0 0.0
5 1.0 0.0 0.0
处理缺失数据
NA处理方法
| 方法 | 说明 |
|————-|————————————————————————|
| dropna | 根据各标签的值是否存在缺失数据对轴标签进行过滤 |
| | 可通过闸值调节对缺失值的容忍度 |
| fillna | 用指定值或插值方法(如ffill或bfill)填充缺失数据 |
| isnull | 返回一个含有布尔值的对象 |
| notnull | 和isnull相反 |
滤除缺失数据
Series中的处理方法
1
2
3
4
5
6
7# dropna方法,不改变obj
from numpy import nan as NA
data = pd.Series([1, NA, 3.5, NA, 7])
print data.dropna()
print
# 用notnull方法
print data[data.notnull()]1
2
3
4
5
6
7
80 1.0
2 3.5
4 7.0
dtype: float64
0 1.0
2 3.5
4 7.0
dtype: float64DF中的处理方法
1
2
3
4
5
6data = pd.DataFrame([[1,6.5,3], [1,NA, NA],
[NA,NA,NA], [NA, 6.5, 3]])
cleaned = data.dropna()
print data
print
print cleaned1
2
3
4
50 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.5 3.0:
1
20 1 2
0 1.0 6.5 3.0丢弃全部为NA的行和列
1
2
3
4
5
6
7
8
9
10
11# 丢弃全部NA的那些行
print data
print
print data.dropna(how = 'all')
print
# 丢弃全部NA的那些列
data[4] = NA
print data
print
print data.dropna(axis = 1, how = 'all')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
220 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.5 3.0
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
3 NaN 6.5 3.0
0 1 2 4
0 1.0 6.5 3.0 NaN
1 1.0 NaN NaN NaN
2 NaN NaN NaN NaN
3 NaN 6.5 3.0 NaN
0 1 2
0 1.0 6.5 3.0
1 1.0 NaN NaN
2 NaN NaN NaN
3 NaN 6.5 3.0丢弃
1
2
3
4
5
6
7
8
9
10
11df = pd.DataFrame(np.random.randn(7,3))
df.ix[:4,1] = NA
df.ix[:2, 2] = NA
print df
print
print df.dropna()
print
#thresh参数为一行拥有[参数]个非NA值
print df.dropna(thresh = 3)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
160 1 2
0 1.244206 NaN NaN
1 0.911635 NaN NaN
2 -1.139608 NaN NaN
3 -1.234194 NaN 0.179958
4 0.377242 NaN -0.576781
5 0.208960 -0.745873 0.471753
6 -0.395019 -0.442547 -0.354554
0 1 2
5 0.208960 -0.745873 0.471753
6 -0.395019 -0.442547 -0.354554
0 1 2
5 0.208960 -0.745873 0.471753
6 -0.395019 -0.442547 -0.354554
填充缺失数据
fillna方法
基本填充
1
2
3
4
5
6
7
8
9# 参数为填充值
print df.fillna(0)
#对不同列填充不同的值
print df.fillna({1:0.5, 2:-1})
# 直接修改对象
_ = df.fillna(0, inplace = True)
print df1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
240 1 2
0 1.244206 0.000000 0.000000
1 0.911635 0.000000 0.000000
2 -1.139608 0.000000 0.000000
3 -1.234194 0.000000 0.179958
4 0.377242 0.000000 -0.576781
5 0.208960 -0.745873 0.471753
6 -0.395019 -0.442547 -0.354554
0 1 2
0 1.244206 0.500000 -1.000000
1 0.911635 0.500000 -1.000000
2 -1.139608 0.500000 -1.000000
3 -1.234194 0.500000 0.179958
4 0.377242 0.500000 -0.576781
5 0.208960 -0.745873 0.471753
6 -0.395019 -0.442547 -0.354554
0 1 2
0 1.244206 0.000000 0.000000
1 0.911635 0.000000 0.000000
2 -1.139608 0.000000 0.000000
3 -1.234194 0.000000 0.179958
4 0.377242 0.000000 -0.576781
5 0.208960 -0.745873 0.471753
6 -0.395019 -0.442547 -0.354554其他插值方法
1
2
3
4
5
6
7df = pd.DataFrame(np.random.randn(6,3))
df.ix[2:,1] = NA; df.ix[4:, 2] =NA
print df
print
print df.fillna(method = 'ffill')
print
print df.fillna(method = 'ffill', limit = 2)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
230 1 2
0 0.286912 0.421018 -0.387265
1 0.587183 1.687635 0.956850
2 -0.173152 NaN 0.771322
3 -1.854829 NaN -0.338808
4 0.296490 NaN NaN
5 -0.217154 NaN NaN
0 1 2
0 0.286912 0.421018 -0.387265
1 0.587183 1.687635 0.956850
2 -0.173152 1.687635 0.771322
3 -1.854829 1.687635 -0.338808
4 0.296490 1.687635 -0.338808
5 -0.217154 1.687635 -0.338808
0 1 2
0 0.286912 0.421018 -0.387265
1 0.587183 1.687635 0.956850
2 -0.173152 1.687635 0.771322
3 -1.854829 1.687635 -0.338808
4 0.296490 NaN -0.338808
5 -0.217154 NaN -0.338808fillna函数的参数
| 参数 | 说明 |
|————-|—————————————————|
| value | 用于填充缺失值的标量值或字典对象 |
| method | 插值方式。默认 ffill |
| axis | 待填充的轴,默认axis = 0 |
| inplace | 修改调用者对象而不产生副本 |
| limit | 限定可连续填充的最大数量 |
层次化索引
可以在一个轴上拥有多个索引级别
1
2
Chapter 7: 数据规整化
合并数据集
数据库风格的DF合并
简单例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
'data1': range(7)})
df2 = pd.DataFrame({'key': ['a', 'b', 'd'],
'data2':range(3)})
print df1
print
print df2
print
#合并(不显示),重叠列的列名当做键(如key)
print pd.merge(df1,df2)
#显示合并
print pd.merge(df1,df2, on = 'key')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21data1 key
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 a
6 6 b
data2 key
0 0 a
1 1 b
2 2 d
data1 key data2
0 0 b 1
1 1 b 1
2 6 b 1
3 2 a 0
4 4 a 0
5 5 a 0不同列名,分别指定合并
1
2
3
4
5
6
7
8
9
10
11df3 = pd.DataFrame({'lkey': list("bbacaab"),
"data1": range(7)})
df4 = pd.DataFrame({'rkey': list('abd'),
'data2':range(3)})
print df3
print
print df4
print
#合并,连接左右key
print pd.merge(df3,df4,left_on = 'lkey',right_on = 'rkey')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21data1 lkey
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 a
6 6 b
data2 rkey
0 0 a
1 1 b
2 2 d
data1 lkey data2 rkey
0 0 b 1 b
1 1 b 1 b
2 6 b 1 b
3 2 a 0 a
4 4 a 0 a
5 5 a 0 a但是默认用的是inner连接,所以键是交集
how选项
1
2
3print pd.merge(df3, df4, how = 'outer', left_on = 'lkey', right_on = 'rkey' )
print pd.merge(df3, df4, how = 'left', left_on = 'lkey', right_on = 'rkey' )
print pd.merge(df3, df4, how = 'right', left_on = 'lkey', right_on = 'rkey' )1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25data1 lkey data2 rkey
0 0.0 b 1.0 b
1 1.0 b 1.0 b
2 6.0 b 1.0 b
3 2.0 a 0.0 a
4 4.0 a 0.0 a
5 5.0 a 0.0 a
6 3.0 c NaN NaN
7 NaN NaN 2.0 d
data1 lkey data2 rkey
0 0 b 1.0 b
1 1 b 1.0 b
2 2 a 0.0 a
3 3 c NaN NaN
4 4 a 0.0 a
5 5 a 0.0 a
6 6 b 1.0 b
data1 lkey data2 rkey
0 0.0 b 1 b
1 1.0 b 1 b
2 6.0 b 1 b
3 2.0 a 0 a
4 4.0 a 0 a
5 5.0 a 0 a
6 NaN NaN 2 d如果合并的对象有重叠列名,可以用suffixes参数来区分列名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'],
"key2": ['one', 'two', 'one'],
"lval": [1, 2, 3]})
right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'],
"key2": ['one', 'one', 'one', 'two'],
"rval": [4,5,6,7]})
print left
print
print right
print
#多列合并
print pd.merge(left,right, on = ['key1','key2'], how = 'outer')
print
#重复列合并
print pd.merge(left,right, on = 'key1')
#使用suffixes来增加重复列的列名区分部分
print pd.merge(left,right, on = 'key1', suffixes = ('_left', "_right"))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32key1 key2 lval
0 foo one 1
1 foo two 2
2 bar one 3
key1 key2 rval
0 foo one 4
1 foo one 5
2 bar one 6
3 bar two 7
key1 key2 lval rval
0 foo one 1.0 4.0
1 foo one 1.0 5.0
2 foo two 2.0 NaN
3 bar one 3.0 6.0
4 bar two NaN 7.0
key1 key2_x lval key2_y rval
0 foo one 1 one 4
1 foo one 1 one 5
2 foo two 2 one 4
3 foo two 2 one 5
4 bar one 3 one 6
5 bar one 3 two 7
key1 key2_left lval key2_right rval
0 foo one 1 one 4
1 foo one 1 one 5
2 foo two 2 one 4
3 foo two 2 one 5
4 bar one 3 one 6
5 bar one 3 two 7
merge函数的参数
| 参数 | 说明 |
|———————|————————————————————————————————|
| left | 参与合并的左侧DataFrame |
| right | 参与合并的右侧DataFrame |
| how | “inner”,”outer”,”left”,”right”其中之一,默认”inner” |
| on | 用于连接的列名,必须存在于左右两个DF中 |
| | 如果未指定,则用left和right的列名的交集作为连接键 |
| left~on~ | 左侧DF用于连接键的列 |
| right~on~ | 右侧DF用于连接键的列 |
| left~index~ | 将左侧的行索引作为连接键 |
| right~index~ | 将右侧的行索引作为连接键 |
| sort | 根据连接键合并后的数据进行排序,默认True |
| | 在处理大数据时,禁用会获得更好的性能 |
| suffixes | 追加重叠列名的末尾,默认(“~x~”, “~y~”) |
| copy | 设置为False,可以在避免将数据复制到结果数据结构中,默认重视复制 |
索引上的合并
有些情况,可以传入left~index~ = True 或 right~index~ =
True(或都传)以说明索引来用作连接键1
2
3
4
5
6
7
8
9left1 = pd.DataFrame({'key': list('abaabc'),
"value": range(6)})
right1 = pd.DataFrame({'group_val':[3.5,7]},
index = ['a', 'b'])
print left1
print
print right1
print
print pd.merge(left1, right1, left_on = 'key', right_index = True)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
group_val
a 3.5
b 7.0
key value group_val
0 a 0 3.5
2 a 2 3.5
3 a 3 3.5
1 b 1 7.0
4 b 4 7.0层次化索引
1
2
3
4
5
6
7
8
9
10
11
12
13
14lefth = pd.DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio', 'Nevada', "Nevada"],
"key2": [2000,2001,2002,2001,2002],
"data": np.arange(5)})
righth = pd.DataFrame(np.arange(12).reshape(6,2),
index = [['Nevada', 'Nevada', 'Ohio', 'Ohio', 'Ohio', 'Ohio'],
[2001, 2000, 2000, 2000, 2001, 2002]],
columns = ['event1', 'event2'])
print lefth
print
print righth
print
#必须以列表的形式指明 用于合并键的多个列
print pd.merge(lefth, righth, left_on = ['key1', 'key2'],right_index = True)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21data key1 key2
0 0 Ohio 2000
1 1 Ohio 2001
2 2 Ohio 2002
3 3 Nevada 2001
4 4 Nevada 2002
event1 event2
Nevada 2001 0 1
2000 2 3
Ohio 2000 4 5
2000 6 7
2001 8 9
2002 10 11
data key1 key2 event1 event2
0 0 Ohio 2000 4 5
0 0 Ohio 2000 6 7
1 1 Ohio 2001 8 9
2 2 Ohio 2002 10 11
3 3 Nevada 2001 0 1join方法,更方便的实现索引合并
1
2
3
4
5
6
7
8
9
10
11
12
13left2 = pd.DataFrame([[1,2], [3,4], [5,6]],index = ['a', 'c', 'a'],
columns = ['Ohio','Nevada'])
right2 = pd.DataFrame([[7,8],[9, 10],[11,12],[13, 14]],
index = ['b', 'c', 'd', 'e' ], columns = ['Missouri', 'Alabama'])
print left2
print
print right2
print
#merge
print pd.merge(left2, right2, how = 'outer', left_index = True, right_index = True)
#join
print left2.join(right2, how = 'outer')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25Ohio Nevada
a 1 2
c 3 4
a 5 6
Missouri Alabama
b 7 8
c 9 10
d 11 12
e 13 14
Ohio Nevada Missouri Alabama
a 1.0 2.0 NaN NaN
a 5.0 6.0 NaN NaN
b NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0
d NaN NaN 11.0 12.0
e NaN NaN 13.0 14.0
Ohio Nevada Missouri Alabama
a 1.0 2.0 NaN NaN
a 5.0 6.0 NaN NaN
b NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0
d NaN NaN 11.0 12.0
e NaN NaN 13.0 14.0参数DF的索引和调用者DF的某一列连接
1
2
3
4
5print left1
print
print right1
print
print left1.join(right1, on = 'key')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
group_val
a 3.5
b 7.0
key value group_val
0 a 0 3.5
1 b 1 7.0
2 a 2 3.5
3 a 3 3.5
4 b 4 7.0
5 c 5 NaN几个DF索引合并
1
2
3
4
5
6another = pd.DataFrame([[7,8], [9, 10], [11, 12], [16, 17]],
index = ['a', 'c', 'e', 'f'],
columns = ["new York", "Oregon"])
print left2.join([right2, another])
print
print left2.join([right2, another], how = 'outer')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Ohio Nevada Missouri Alabama new York Oregon
a 1.0 2.0 NaN NaN 7.0 8.0
a 5.0 6.0 NaN NaN 7.0 8.0
b NaN NaN 7.0 8.0 NaN NaN
c 3.0 4.0 9.0 10.0 9.0 10.0
d NaN NaN 11.0 12.0 NaN NaN
e NaN NaN 13.0 14.0 11.0 12.0
f NaN NaN NaN NaN 16.0 17.0
Ohio Nevada Missouri Alabama new York Oregon
a 1.0 2.0 NaN NaN 7.0 8.0
a 5.0 6.0 NaN NaN 7.0 8.0
b NaN NaN 7.0 8.0 NaN NaN
c 3.0 4.0 9.0 10.0 9.0 10.0
d NaN NaN 11.0 12.0 NaN NaN
e NaN NaN 13.0 14.0 11.0 12.0
f NaN NaN NaN NaN 16.0 17.0
轴向连接
concatenation函数(合并原始Numpy数组)
1
2
3
4arr = np.arange(12).reshape(3,4)
print arr
print
print np.concatenate([arr,arr], axis = 1)1
2
3[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]:
1
2
3[[ 0 1 2 3 0 1 2 3]
[ 4 5 6 7 4 5 6 7]
[ 8 9 10 11 8 9 10 11]]pandas中的连接
需要考虑的因素
- 如果个对象其他轴上的索引不同, 那些轴应该是做并集还是交集
- 结果对象中的分组需要各不相同吗
- 用于连接的轴重要吗
concat函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15s1 = pd.Series([0,1], index = ['a', 'b'])
s2 = pd.Series([2, 3, 4], index = list('cde'))
s3 = pd.Series([5, 6], index = ['f', 'g'])
print s1
print
print s2
print
print s3
print
#concat默认在axis = 0下工作,返回一个Series
print pd.concat([s1, s2, s3])
#在axis = 1上作用,会变成一个DF
print pd.concat([s1, s2, s3], axis = 1)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29a 0
b 1
dtype: int64
c 2
d 3
e 4
dtype: int64
f 5
g 6
dtype: int64
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
0 1 2
a 0.0 NaN NaN
b 1.0 NaN NaN
c NaN 2.0 NaN
d NaN 3.0 NaN
e NaN 4.0 NaN
f NaN NaN 5.0
g NaN NaN 6.0Cconcet 默认外连接, join和 join~axes参数~
1
2
3
4
5
6
7
8
9
10
11
12
13s4 = pd.concat([s1 *5, s3])
print s4
print
print pd.concat([s1, s4], axis = 1)
print
#使用内连接
print pd.concat([s1, s4], join = 'inner')
#指定要在其他州上使用的索引
#s4的f g行就消失了,多了 c e行
print pd.concat([s1, s4], axis = 1, join_axes = [['a', 'c', 'b', 'e']]) #join_axes是list里有索引list1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24a 0
b 5
f 5
g 6
dtype: int64
0 1
a 0.0 0
b 1.0 5
f NaN 5
g NaN 6
a 0
b 1
a 0
b 5
f 5
g 6
dtype: int64
0 1
a 0.0 0.0
c NaN NaN
b 1.0 5.0
e NaN NaN创造层次化, key参数
1
2
3
4
5
6
7
8
9
10
11
12#key参数 将三个Series分层
result = pd.concat([s1, s2, s3], keys = ['one', 'two', 'three'])
print result
print
#unstack 将分层的Series变成DF
print result.unstack()
print
#沿着axis = 1对Series进行合并,keys变成DataFrame的列头
print "就是上面的行列置换:"
print pd.concat([s1, s2, s3], axis = 1, keys = ['one', 'two', 'three'])1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23one a 0
b 1
two c 2
d 3
e 4
three f 5
g 6
dtype: int64
a b c d e f g
one 0.0 1.0 NaN NaN NaN NaN NaN
two NaN NaN 2.0 3.0 4.0 NaN NaN
three NaN NaN NaN NaN NaN 5.0 6.0
就是上面的行列置换
one two three
a 0.0 NaN NaN
b 1.0 NaN NaN
c NaN 2.0 NaN
d NaN 3.0 NaN
e NaN 4.0 NaN
f NaN NaN 5.0
g NaN NaN 6.0DF中的层次化
1
2
3
4
5
6
7
8
9df1 = pd.DataFrame(np.arange(6).reshape(3,2), index = list('abc'),
columns = ['one', 'two'])
df2 = pd.DataFrame(5+ np.arange(4).reshape(2,2), index = ['a', 'c'],
columns = ['three', 'four'])
print pd.concat([df1, df2], axis = 1, keys = ['level1', 'level2'])
print
#参数如果不是列表是字典,则字典的key会当成keys的值,value会是做对应的对象
print pd.concat({'level1': df1, 'level2' : df2}, axis = 1)1
2
3
4
5
6
7
8
9
10
11level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
忽略行索引, ignore~index参数~
1
2
3
4
5
6
7
8
9
10
11
12df1 = pd.DataFrame(np.random.randn(3,4), columns = ['a', 'b', 'c', 'd'])
df2 = pd.DataFrame(np.random.randn(2,3), columns = ['b', 'd', 'a'])
print df1
print
print df2
print
#传入ignore_index = True来忽略行索引
print pd.concat([df1, df2], ignore_index = True)
print
print "如果不传入ignore_index的话"
print pd.concat([df1, df2])1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23a b c d
0 1.135556 0.913366 -0.343349 -0.552474
1 -0.092713 -0.396608 0.862300 0.676480
2 -0.489371 -0.738190 -0.706639 0.545143
b d a
0 0.318872 1.53383 -1.174550
1 1.939470 -1.25647 -0.083473
a b c d
0 1.135556 0.913366 -0.343349 -0.552474
1 -0.092713 -0.396608 0.862300 0.676480
2 -0.489371 -0.738190 -0.706639 0.545143
3 -1.174550 0.318872 NaN 1.533830
4 -0.083473 1.939470 NaN -1.256470
如果不传入ignore_index的话
a b c d
0 1.135556 0.913366 -0.343349 -0.552474
1 -0.092713 -0.396608 0.862300 0.676480
2 -0.489371 -0.738190 -0.706639 0.545143
0 -1.174550 0.318872 NaN 1.533830
1 -0.083473 1.939470 NaN -1.256470concat函数的参数
| 参数 | 说明 |
|—————————-|——————————————————————————|
| objs | 参与连接的对象列表或者字典。 |
| | 唯一必要的参数 |
| axis | 指明连接的轴向,默认为0 |
| join | “inner” “outer” 默认为”outer” |
| join~axes~ | 指明用于其他n-1条轴的索引 |
| | 不执行并集/交集运算 |
| keys | 与连接对象有关的值,用于形成层次化索引 |
| | 可以使任意值的列表或数组,元组,数组列表 |
| levels | 指定用于层次化索引各级上的索引,如果设置了keys |
| names | 用于创造分层级别的名字,如果设置了keys和(或)levels |
| verify~integrity~ | 检查见过对象新轴上的重复情况,如果发现则引起异常 |
| | 默认为(false)允许重复 |
| ignore~index~ | 不保留连接轴上的索引 |
| | 产生一组新索引range(total~length~) |
合并重叠数据
使用Numpy的where函数,用于表达矢量化的if-else
1
2
3
4
5
6
7
8
9
10
11a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan],
index = list ('fedcba'))
b = pd.Series(np.arange(len(a)), dtype = np.float64,
index = list('fedcba'))
b[-1] = np.nan
print a
print
print b
print
print np.where(pd.isnull(a), b, a )1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17f NaN
e 2.5
d NaN
c 3.5
b 4.5
a NaN
dtype: float64
f 0.0
e 1.0
d 2.0
c 3.0
b 4.0
a NaN
dtype: float64
[ 0. 2.5 2. 3.5 4.5 nan]Series的combine~first方法~,会进行数据对齐?
1
print a.combine_first(b)
1
2
3
4
5
6
7f 0.0
e 2.5
d 2.0
c 3.5
b 4.5
a NaN
dtype: float64对于 DF,combine~first~,对缺失值”打补丁”
1
2
3
4
5
6
7
8
9
10
11df1 = pd.DataFrame({'a': [1, np.nan, 5, np.nan],
'b': [np.nan, 2, np.nan, 6],
"c": range(2, 18, 4)})
df2 = pd.DataFrame({'a': [5, 4, np.nan, 3, 7],
'b': [np.nan, 3, 4, 6, 8]})
print df1
print
print df2
print
print df1.combine_first(df2)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19a b c
0 1.0 NaN 2
1 NaN 2.0 6
2 5.0 NaN 10
3 NaN 6.0 14
a b
0 5.0 NaN
1 4.0 3.0
2 NaN 4.0
3 3.0 6.0
4 7.0 8.0
a b c
0 1.0 NaN 2.0
1 4.0 2.0 6.0
2 5.0 4.0 10.0
3 3.0 6.0 14.0
4 7.0 8.0 NaN
重塑和轴向旋转
重塑层次化索引
层次化索引为DF数据的重排提供了一种良好的一致性方式
- stack: 将数据的列”旋转”为行
- unstack: 将数据的行”旋转”为列
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14#需要增加name,所以用 pd.Index
data = pd.DataFrame(np.arange(6).reshape(2,3),
index = pd.Index(['Ohio', 'Colorado'], name = 'state'),
columns = pd.Index(['one', 'two', 'three'], name = 'number'))
print data
print
#用stack来将列转化成行,得到一个层次化的Series
result = data.stack()
print result
#对于一个层次化的Series,用unstack将其重排为一个DataFrame
print result.unstack()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17number one two three
state
Ohio 0 1 2
Colorado 3 4 5
state number
Ohio one 0
two 1
three 2
Colorado one 3
two 4
three 5
dtype: int32
number one two three
state
Ohio 0 1 2
Colorado 3 4 5默认操作是最内层,传入分层级别的编号或名称
1
2
3
4
5print result
print
print result.unstack(1) # 1 代表着第二层,从0开始
print
print result.unstack('state')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18state number
Ohio one 0
two 1
three 2
Colorado one 3
two 4
three 5
dtype: int32
number one two three
state
Ohio 0 1 2
Colorado 3 4 5
state Ohio Colorado
number
one 0 3
two 1 4
three 2 5如果不是左右级别值都能在分组中找到,则unstack操作会引入缺失数据
1
2
3
4
5
6s1 = pd.Series([0,1,2,3],index = list('abcd'))
s2 = pd.Series([4,5,6], index = list('cde'))
data2 = pd.concat([s1, s2], keys = ['one', 'two'])
print data2
print
print data2.unstack()1
2
3
4
5
6
7
8
9
10
11
12one a 0
b 1
c 2
d 3
two c 4
d 5
e 6
dtype: int64
a b c d e
one 0.0 1.0 2.0 3.0 NaN
two NaN NaN 4.0 5.0 6.0stack反而会滤除缺失数据,所以该运算是可逆的
1
2
3
4
5
6
7print data2.unstack()
print
print data2.unstack().stack()
print
#不滤除
print data2.unstack().stack(dropna = False)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24a b c d e
one 0.0 1.0 2.0 3.0 NaN
two NaN NaN 4.0 5.0 6.0
one a 0.0
b 1.0
c 2.0
d 3.0
two c 4.0
d 5.0
e 6.0
dtype: float64
one a 0.0
b 1.0
c 2.0
d 3.0
e NaN
two a NaN
b NaN
c 4.0
d 5.0
e 6.0
dtype: float64对DF进行unstack操作,旋转轴的级别或变成结果中的最低级别
1
2
3
4
5
6
7df = pd.DataFrame({'left': result,'right': result + 5},
columns = pd.Index(['left', 'right'], name = 'side'))
print df
print
print df.unstack('state')
print
print df.unstack('state').stack('side')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24side left right
state number
Ohio one 0 5
two 1 6
three 2 7
Colorado one 3 8
two 4 9
three 5 10
side left right
state Ohio Colorado Ohio Colorado
number
one 0 3 5 8
two 1 4 6 9
three 2 5 7 10
state Ohio Colorado
number side
one left 0 3
right 5 8
two left 1 4
right 6 9
three left 2 5
right 7 10
将”长格式”旋转成”宽格式”
时间序列数据为”长格式(long)”或”堆叠格式(stacked)”储存
使用DF.pivoted方法来处理,可以实现转换
待补充
数据转换
移除重复数据
DF的duplicated方法返回一个布尔Series,表示各行是否是重复行
1
2
3
4
5
6
7data = pd.DataFrame({'k1': ['one'] * 3 + ['two'] * 4,
'k2': [1,1,2,3,3,4,4]})
print data
print
#duplicated方法
print data.duplicated()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17k1 k2
0 one 1
1 one 1
2 one 2
3 two 3
4 two 3
5 two 4
6 two 4
0 False
1 True
2 False
3 False
4 True
5 False
6 True
dtype: boolDF的drop~duplicates方法~,返回一个移除了重复行的DF
duplicated和drop~duplicated默认会判断全部列~
也可以指定部分列进行重复项判断,默认保留第一次出现的值组合
也可以传入take~last~ = True来保留最后一个
1
2
3
4
5
6
7
8data['v1'] = range(7)
print data
print
#指定根据k1来判断
print data.drop_duplicates(['k1'])
print
#根据k1和k2判断,但是保留最后一项
print data.drop_duplicates(['k1', 'k2'], take_last = True)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18k1 k2 v1
0 one 1 0
1 one 1 1
2 one 2 2
3 two 3 3
4 two 3 4
5 two 4 5
6 two 4 6
k1 k2 v1
0 one 1 0
3 two 3 3
k1 k2 v1
1 one 1 1
2 one 2 2
4 two 3 4
6 two 4 6
利用函数或映射进行数据转换
使用map函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20data = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon', 'Pastrami',
'corned beef', 'Bacon', 'pastrami', 'honey ham',
'nova lox'],
'ounces': [4,3,12,6,7.5,8,3,5,6]})
print data
print
#需要添加的列表,表示该肉类食物来源的动物类型
meat_to_animal = {'bacon': 'pig',
'pulled pork': 'pig',
'pastrami': 'cow',
'corned beef': 'cow',
'honey ham': 'pig',
'nova lox': 'salmon'}
#Series的map方法接受一个函数或者含有映射关系的字典型对象
data['animal'] = data['food'].map(str.lower).map(meat_to_animal)
print data
print
#直接传入一个直接完成全部工作的函数
print data['food'].map(lambda x: meat_to_animal[x.lower()])1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32food ounces
0 bacon 4.0
1 pulled pork 3.0
2 bacon 12.0
3 Pastrami 6.0
4 corned beef 7.5
5 Bacon 8.0
6 pastrami 3.0
7 honey ham 5.0
8 nova lox 6.0
food ounces animal
0 bacon 4.0 pig
1 pulled pork 3.0 pig
2 bacon 12.0 pig
3 Pastrami 6.0 cow
4 corned beef 7.5 cow
5 Bacon 8.0 pig
6 pastrami 3.0 cow
7 honey ham 5.0 pig
8 nova lox 6.0 salmon
0 pig
1 pig
2 pig
3 cow
4 cow
5 pig
6 cow
7 pig
8 salmon
Name: food, dtype: object
替换值
repalce方法来替换
1
2
3
4
5
6
7
8
9
10data = pd.Series([1, -999, 2, -999, -1000, 3])
print data
print
print data.replace(-999, np.nan)
print
print data.replace([-999,-1000], np.nan) #-999和-1000替换成NA
print
print data.replace([-999, -1000], [np.nan, 0]) #分别替换
print
print data.replace({-999: np.nan, -1000: 0})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
390 1
1 -999
2 2
3 -999
4 -1000
5 3
dtype: int64
0 1.0
1 NaN
2 2.0
3 NaN
4 -1000.0
5 3.0
dtype: float64
0 1.0
1 NaN
2 2.0
3 NaN
4 NaN
5 3.0
dtype: float64
0 1.0
1 NaN
2 2.0
3 NaN
4 0.0
5 3.0
dtype: float64
0 1.0
1 NaN
2 2.0
3 NaN
4 0.0
5 3.0
dtype: float64
重命名轴索引
对轴索引的操作 map 和rename
1 | data = pd.DataFrame(np.arange(12).reshape(3,4), |
1 | ['OHIO' 'COLORADO' 'NEW YORK'] |
如果希望就地修改数据,传入 inplace = True 即可
离散化和面元划分
连续数据常常被离散化或者拆分成”面元(bin)” pd.cut
1 | # 假设希望划分下列年龄 |
1 | [(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]] |
cut函数返回一个特殊的Categorical对象,
其拥有不同分类名字的levels数组
以及一个为数据进行标号的labels属性
1
2
3
4
5
6
7
8# labels是区间的序号
print cuts.labels
print
#levels是所有的区间
print cuts.levels
print
#数在个区间的个数
print pd.value_counts(cuts)1
[0 0 0 1 0 0 2 1 3 2 2 1]
:
1
Index([u'(18, 25]', u'(25, 35]', u'(35, 60]', u'(60, 100]'], dtype='object')
:
1
2
3
4
5(18, 25] 5
(35, 60] 3
(25, 35] 3
(60, 100] 1
dtype: int64括号表示开区间,方括号表示闭区间
那边是闭区间可以通过 right = False 来修改
1
2print pd.cut(ages, [18, 26, 36, 61, 100],right = False)
print pd.cut(ages, [18,26,36,61, 100], right = True)1
2
3
4
5
6[[18, 26), [18, 26), [18, 26), [26, 36), [18, 26), ..., [26, 36), [61, 100), [36, 61), [36, 61), [26, 36)]
Length: 12
Categories (4, object): [[18, 26) < [26, 36) < [36, 61) < [61, 100)]
[(18, 26], (18, 26], (18, 26], (26, 36], (18, 26], ..., (26, 36], (36, 61], (36, 61], (36, 61], (26, 36]]
Length: 12
Categories (4, object): [(18, 26] < (26, 36] < (36, 61] < (61, 100]]添加标签,代替显示区间,使用cut的labels参数
1
2
3
4
5
6
7
8group_names = ['Youth', 'YoungAdult', 'MiddleAged', 'Senior']
cuts1 = pd.cut(ages, bins, labels = group_names)
print cuts1
#labels属性没有变化
print cuts1.labels
#levels属性变成标签
print
print cuts1.levels1
2
3
4[Youth, Youth, Youth, YoungAdult, Youth, ..., YoungAdult, Senior, MiddleAged, MiddleAged, YoungAdult]
Length: 12
Categories (4, object): [Youth < YoungAdult < MiddleAged < Senior]
[0 0 0 1 0 0 2 1 3 2 2 1]:
1
Index([u'Youth', u'YoungAdult', u'MiddleAged', u'Senior'], dtype='object')
如果cut中传入的是面元数量而不是确切的面元边界
会根据数据的最小值和最大值计算等长面元
1
2
3
4data = np.random.rand(20)
# 4 为要分的分数
# precision 为小数精度
print pd.cut(data,4, precision = 3)1
2
3[(0.299, 0.526], (0.299, 0.526], (0.299, 0.526], (0.752, 0.979], (0.072, 0.299], ..., (0.072, 0.299], (0.072, 0.299], (0.526, 0.752], (0.299, 0.526], (0.072, 0.299]]
Length: 20
Categories (4, object): [(0.072, 0.299] < (0.299, 0.526] < (0.526, 0.752] < (0.752, 0.979]]
qcut类似于cut函数,可以根据样本分位数来划分
1
2
3
4
5
6data = np.random.randn(1000) # 正态分布
cats = pd.qcut(data,4) # 不是区间等分,而是数量等分
print cats.levels
print
print pd.value_counts(cats)1
2
3Index([u'[-3.152, -0.722]', u'(-0.722, -0.00102]', u'(-0.00102, 0.646]',
u'(0.646, 3.58]'],
dtype='object'):
1
2
3
4
5(0.646, 3.58] 250
(-0.00102, 0.646] 250
(-0.722, -0.00102] 250
[-3.152, -0.722] 250
dtype: int64qcut可以设置自定义的分位数(0到1之间的数值,包含端点),数量等分
1
2
3
4cuts1 = pd.qcut(data, [0, 0.1, 0.5, 0.9, 1.0])
print cuts1
print
print pd.value_counts(cuts1)1
2
3[(-0.00102, 1.169], (1.169, 3.58], (-0.00102, 1.169], (-0.00102, 1.169], (-0.00102, 1.169], ..., (-1.275, -0.00102], (-1.275, -0.00102], (-1.275, -0.00102], (-1.275, -0.00102], (-0.00102, 1.169]]
Length: 1000
Categories (4, object): [[-3.152, -1.275] < (-1.275, -0.00102] < (-0.00102, 1.169] < (1.169, 3.58]]:
1
2
3
4
5(-0.00102, 1.169] 400
(-1.275, -0.00102] 400
(1.169, 3.58] 100
[-3.152, -1.275] 100
dtype: int64
检测和过滤异常值
异常值(outlier)的过滤或换算基本就是数组运算
1 | np.random.seed(12345) |
1 | 0 1 2 3 |
利用这些条件来限制值在区间内(np.sign返回符号(-1,0,1))
1
2data[np.abs(data) > 3] = np.sign(data) * 3
print data.describe()1
2
3
4
5
6
7
8
90 1 2 3
count 1000.000000 1000.000000 1000.000000 1000.000000
mean -0.067623 0.068473 0.025153 -0.002081
std 0.995485 0.990253 1.003977 0.989736
min -3.000000 -3.000000 -3.000000 -3.000000
25% -0.774890 -0.591841 -0.641675 -0.644144
50% -0.116401 0.101143 0.002073 -0.013611
75% 0.616366 0.780282 0.680391 0.654328
max 3.000000 2.653656 3.000000 3.000000
排列和随机采样
numpy.random.permutation函数可以实现对Series或者DF的列的排序工作
通过需要排列的轴长度调用permutation,产生一个新顺序的整数数组
1
2
3df = pd.DataFrame(np.arange( 5 * 4).reshape(5, 4))
sampler = np.random.permutation(5)
print sampler1
[1 3 0 2 4]
然后通过ix索引操作或者take函数中使用该数组
1
2
3
4print df
print
print df.take(sampler)
print1
2
3
4
5
6
7
8
9
10
11
12
130 1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9 10 11
3 12 13 14 15
4 16 17 18 19
0 1 2 3
1 4 5 6 7
3 12 13 14 15
0 0 1 2 3
2 8 9 10 11
4 16 17 18 19通过permutation来随机选取子集
1
print df.take(np.random.permutation(len(df))[:3])
1
2
3
40 1 2 3
1 4 5 6 7
0 0 1 2 3
4 16 17 18 19替换方式来产生样本 np.random.randint
1
2
3
4
5
6
7bag = np.array([5,7,-1,6,5])
sampler = np.random.randint(0, len(bag), size = 10)
print sampler
print
draws = bag.take(sampler)
print draws1
[3 0 4 1 1 2 3 0 1 2]
:
1
[ 6 5 5 7 7 -1 6 5 7 -1]
计算指标/哑变量
常用于统计建模或机器学习的转换方式
将 分类标量 转换成 哑变量矩阵 或 指标矩阵
如果DF某一列含有K个不同的值,则可以派生出一个
pandas中 get~dummies函数可以实现该功能~
1
2
3
4
5df = pd.DataFrame({'key': list('bbacab'),
'data1': range(3,9)})
print df
print
print pd.get_dummies(df['key'])1
2
3
4
5
6
7
8
9
10
11
12
13
14
15data1 key
0 3 b
1 4 b
2 5 a
3 6 c
4 7 a
5 8 b
a b c
0 0.0 1.0 0.0
1 0.0 1.0 0.0
2 1.0 0.0 0.0
3 0.0 0.0 1.0
4 1.0 0.0 0.0
5 0.0 1.0 0.0给指标DF的列一个前缀,以便能够跟其他数据进行合并 prefix参数
1
2
3
4
5
6dummies = pd.get_dummies(df['key'], prefix = 'key')
print dummies
print
# 将 df的 data1列与 dummies合并,就是左右合并,dummies的列为前缀_[a, b,c...]
df_with_dummy = df[['data1']].join(dummies)
print df_with_dummy1
2
3
4
5
6
7
8
9
10
11
12
13
14
15key_a key_b key_c
0 0.0 1.0 0.0
1 0.0 1.0 0.0
2 1.0 0.0 0.0
3 0.0 0.0 1.0
4 1.0 0.0 0.0
5 0.0 1.0 0.0
data1 key_a key_b key_c
0 3 0.0 1.0 0.0
1 4 0.0 1.0 0.0
2 5 1.0 0.0 0.0
3 6 0.0 0.0 1.0
4 7 1.0 0.0 0.0
5 8 0.0 1.0 0.0对于某行同属于多个分类的处理 没明白</span>
读取数据
1
2
3
4mnames = ['movie_id', 'title', 'genres']
movies = pd.read_table('pydata-book-master\ch02\movielens\movies.dat',
sep = '::', header = None, names = mnames)
print movies[:10]1
2
3
4
5
6
7
8
9
10
11movie_id title genres
0 1 Toy Story (1995) Animation|Children's|Comedy
1 2 Jumanji (1995) Adventure|Children's|Fantasy
2 3 Grumpier Old Men (1995) Comedy|Romance
3 4 Waiting to Exhale (1995) Comedy|Drama
4 5 Father of the Bride Part II (1995) Comedy
5 6 Heat (1995) Action|Crime|Thriller
6 7 Sabrina (1995) Comedy|Romance
7 8 Tom and Huck (1995) Adventure|Children's
8 9 Sudden Death (1995) Action
9 10 GoldenEye (1995) Action|Adventure|Thriller给每个genre添加指标变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# 从数据集中抽取出不同的genre值(注意巧用set.union)
genre_iter = (set(x.split('|')) for x in movies.genres)
genres = sorted(set.union(*genre_iter))
#从一个全零DF开始构建指标DF
dummies = pd.DataFrame(np.zeros((len(movies), len(genres))),
columns = genres)
#接下来,迭代每部电影并将dummies各行的项设置为1
for i, gen in enumerate(movies.genres):
dummies.ix[i, gen.split('|')] = 1
# 然后,在将其与movies合并起来
movies_windic = movies.join(dummies.add_prefix('Genre_'))
print movies_windic.ix[0]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22movie_id 1
title Toy Story (1995)
genres Animation|Children's|Comedy
Genre_Action 0
Genre_Adventure 0
Genre_Animation 1
Genre_Children's 1
Genre_Comedy 1
Genre_Crime 0
Genre_Documentary 0
Genre_Drama 0
Genre_Fantasy 0
Genre_Film-Noir 0
Genre_Horror 0
Genre_Musical 0
Genre_Mystery 0
Genre_Romance 0
Genre_Sci-Fi 0
Genre_Thriller 0
Genre_War 0
Genre_Western 0
Name: 0, dtype: object对统计应用有用的秘诀是
结合get~dummies~ 和诸如cut之类的离散化函数
1
2
3
4
5values = np.random.rand(10)
print values
bins = [0, 0.2, 0.4, 0.6, 0.8, 1.]
print pd.get_dummies(pd.cut(values, bins))1
2
3
4
5
6
7
8
9
10
11
12
13[ 0.48344916 0.11734259 0.07557764 0.21863608 0.90953758 0.38441329
0.08127486 0.95663486 0.04387138 0.14476865]
(0, 0.2] (0.2, 0.4] (0.4, 0.6] (0.6, 0.8] (0.8, 1]
0 0.0 0.0 1.0 0.0 0.0
1 1.0 0.0 0.0 0.0 0.0
2 1.0 0.0 0.0 0.0 0.0
3 0.0 1.0 0.0 0.0 0.0
4 0.0 0.0 0.0 0.0 1.0
5 0.0 1.0 0.0 0.0 0.0
6 1.0 0.0 0.0 0.0 0.0
7 0.0 0.0 0.0 0.0 1.0
8 1.0 0.0 0.0 0.0 0.0
9 1.0 0.0 0.0 0.0 0.0
字符串操作
字符串对象方法
一些处理方法例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32val = 'a,b, guido'
print val.split(',')
#结合 strip(用于修剪空白符(包括换行符))一起使用
pieces = [x.strip() for x in val.split(',')]
print pieces
print
#利用加法,可以将这些子字符串以双冒号分隔符的形式连接起来
first, second, third = pieces
print first + '::' + second + "::" + third
print
#但是这种不很实用,一种更快的ptyhon方法就是
#向字符串"::"的join方法传入一个列表或者元组
print "::".join(pieces)
print
#另一个方法就是子串定位,检测子串的最佳方式是利用python的in关键字(index和find也可以用)
print 'guido' in val
print
print val.index(',') # 找不到会引发异常
print val.find(':') # 找不到会返回-1
#count返回指定子串出现的次数
print val.count(',')
#repalce用于将指定模式替换另一种模式,
#常用于删除模式: 传入空字符串
print val.replace(',', '::')
print
print val.replace(',', '')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15['a', 'b', ' guido']
['a', 'b', 'guido']
a::b::guido
a::b::guido
True
1
-1
2
a::b:: guido
ab guidoPython 内置的字符串方法
| 方法 | 说明 |
|—————————|—————————————————————————————-|
| count | 返回子串在字符串中的出现次数(非重叠) |
| endswith startswith | 如果字符串以某个后缀结尾(开头),则返回True |
| join | 将字符串用于连接其他字符串序列的分隔符 |
| index | 如果在字符串中找到子串,则返回子串第一个字符所在的位置 |
| | 如果没有找到,则引发VauleError |
| find | 如果在字符串中找到子串,则返回第一个发现子串的第一个字符的位置 |
| | 如果没有找到,则返回-1 |
| rfind | 如果在字符串中找到子串,则返回最后一个发现的子串的第一字符所在的位置 |
| | 如果没有找到,则返回-1 |
| raplace | 用另一个字符串替换指定子串 |
| strip rstrip lstrip | 去除空白符(包括换行符) |
| | 相当于对个元素执行了x.strip() (以及rstrip lstrip) |
| split | 通过指定的分隔符将字符串拆分为一组子串 |
| lower upper | 将字符转换成小写或大写 |
| ljust rjust | 用空格(或其他字符)填充字符串的空白侧以返回符合的最低宽度字符串 |
正则表达式
re模块的函数分为三大块
- 模式匹配
- 替换
- 拆分
拆分例子
1
2
3
4
5
6
7
8
9import re
text = "foo bar\t baz \tqux"
print re.split('\s+', text)
# 也可以先编译regex以得到一个可重用的regex对象
regex = re.compile('\s+')
print regex.split(text)
# 如果只想得到匹配regex的所有模式,则可以用findall方法
print regex.findall(text)1
2
3['foo', 'bar', 'baz', 'qux']
['foo', 'bar', 'baz', 'qux']
[' ', '\t ', ' \t']因为re.split自带先re.compile之后在拆分
对于许多字符串使用同一正则表达式,先通过re.compile创建regex对象,更节省时间
match search findall 之间的不同
findall会返回字符串中所有的匹配项
search返回第一个匹配项
match更严格,只匹配字符串的首部
区别例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20text = """Dave dave@google.com
Steve steve@gmail.com
Rob rob@gmail.com
Ryan ryan@yahoo.com"""
pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'
# re.IGNORECASS的作用是使正则表达式对大小写不敏感
regex = re.compile(pattern, flags = re.IGNORECASE)
#对text使用findall打到 一组电子邮件地址
print regex.findall(text)
#search匹配字一个地址
# m 是个对象
m = regex.search(text)
print text[m.start(): m.end()]
#regex.match将返回None,因为他只匹配字符串开头
print regex.match(text)1
2
3['dave@google.com', 'steve@gmail.com', 'rob@gmail.com', 'ryan@yahoo.com']
dave@google.com
None
sub方法 会将匹配到的模式替换成指定字符串,返回得到的新字符串
1
print regex.sub('REDACTED', text)
1
2
3
4Dave REDACTED
Steve REDACTED
Rob REDACTED
Ryan REDACTED如果不止找出数据(电子邮件地址),还要将数据分成几部分(列如 用户名
域名 域后缀)将待分段的模式的各部分用圆括号包起来即可
1
2
3
4
5
6
7
8
9pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
regex = re.compile(pattern, flags = re.IGNORECASE)
#可以通过groups方法返回一个由模式各段组成的元组
m = regex.match('wesm@bright.net')
print m.groups()
#对于findall会返回一个元组列表
print regex.findall(text)1
2('wesm', 'bright', 'net')
[('dave', 'google', 'com'), ('steve', 'gmail', 'com'), ('rob', 'gmail', 'com'), ('ryan', 'yahoo', 'com')]
sub还能通过诸如\\1 \\2之类的特殊符号访问各匹配项中的分组
1
print regex.sub(r'Username: \1, Domain: \2, Sufix : \3',text)
1
2
3
4Dave Username: dave, Domain: google, Sufix : com
Steve Username: steve, Domain: gmail, Sufix : com
Rob Username: rob, Domain: gmail, Sufix : com
Ryan Username: ryan, Domain: yahoo, Sufix : com正则扩展
为各个匹配分组加上一个名字
1
2
3
4
5
6
7
8
9
10regex = re.compile(r"""
(?P<username>[A-Z0-9._%+-]+)
@
(?P<domain>[A-Z0-9.-]+)
\.
(?P<suffix>[A-Z]{2,4})""", flags = re.IGNORECASE|re.VERBOSE)
m = regex.match('wesm@bright.net')
#这种正则表达式所产生的匹配项对象可以得到一个简单易用的带有分组名称的字典
print m.groupdict()1
{'username': 'wesm', 'domain': 'bright', 'suffix': 'net'}
正则表达式方法
| 方法 | 说明 |
|---|---|
| findall finditer | 返回字符串中所有的非重叠匹配模式 |
| findall返回所有模式组成的列表 | |
| finditer返回一个迭代器逐个返回 | |
| match | 从字符串起始位置匹配,还可以对模式各部分分组 |
| 如果匹配到模式,则返回一个匹配项对象,否则返回None | |
| search | 如果找到返回第一个匹配对象 |
| 与match不同,其可以位于字符串的任意位置,而不仅仅是起始处 | |
| split | 根据找到的字符串拆分成数段 |
| sub subn | 将字符串中所有(sub)或前n个(subn)替换成指定表达式 |
| 在替换字符串中可以通过\\1 \\2等符号表示各分组项 |
Pandas中矢量化的字符串函数
矢量化字符串规整化
例子
处理数据
1
2
3
4
5
6data = {'Dave': 'dave@google.com',
'Steve': 'steve@gmail.com',
'Rob': 'rob@gmail.com',
'Wes':np.nan}
data = pd.Series(data)
print data1
2
3
4
5Dave dave@google.com
Rob rob@gmail.com
Steve steve@gmail.com
Wes NaN
dtype: object通过data.map,所有字符串和正则方法(传入函数)各个值,但是NA会报错
为了解决这问题,Series有跳过NA值的字符串方法
Series的Str属性可以访问这些方法
例如通过str.contains检查邮件地址是否含有”gmail”
1
print data.str.contains('gmail')
1
2
3
4
5Dave False
Rob True
Steve True
Wes NaN
dtype: object使用正则表达式,还可以加上任意re选项(如IGNORECASE)
1
2
3print pattern
print
print data.str.findall(pattern, flags = re.IGNORECASE)1
([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})
:
1
2
3
4
5Dave [(dave, google, com)]
Rob [(rob, gmail, com)]
Steve [(steve, gmail, com)]
Wes NaN
dtype: object两种实现矢量化元素获取操作
- str.get
str属性上使用索引
1
2
3
4
5
6
7
8
9
10
11
12matchs = data.str.match(pattern, flags = re.IGNORECASE)
print matchs
print
print matchs.str.get(1)
print
print matchs.str[0]
print
# 也可以子串截取 :和书不一样,截取的不是整个字符串,而是分区的数量
print matchs.str[:5]#+begin~example~ Dave (dave, google, com) Rob (rob,
gmail, com) Steve (steve, gmail, com) Wes NaN dtype:
objectDave google Rob gmail Steve gmail Wes NaN dtype: object
Dave dave Rob rob Steve steve Wes NaN dtype: object
Dave (dave, google, com) Rob (rob, gmail, com) Steve
(steve, gmail, com) Wes NaN dtype: object
#+end~example~
矢量化字符串方法
| 方法 | 说明 |
|---|---|
| cat | 实现元素级的字符串连接操作可指定分隔符 |
| contains | 返回表示各字符串是否含有指定模式的布尔型数组 |
| count | 模式的出现次数 |
| endswith startswith | 相当于对各元素执行x.endswith(pattern)或x.startswith(pattern) |
| findall | 计算各字符串的模式列表 |
| get | 获取各元素第i个字符 |
| join | 根据指定的分隔符将Series中各元素的字符串连接起来 |
| len | 计算各字符串的长度 |
| lower upper | 转换大小写 |
| match | 根据指定的正则表达式对各元素执行re.match |
| pad | 在字符串的左边、右边或左右两边天界空白符 |
| center | 相当于pad(side = ‘both’) |
| repeat | 重复值 |
| 列如s.str.repeat(3)相当于对各个字符串执行x*3 | |
| replace | 用指定字符串替换找到的模式 |
| slice | 对Series中的各个字符串进行子串截取 |
| split | 根据分隔符或正则表达式对字符串进行拆分 |
| strip rstrip lstrip | 去除空白符,包括换行符 |
| 相当于对每个元素执行x.strip() x.rstrip() x.strip() |
示例: USDA食品数据库 待补充</span> [示例-usda食品数据库]
Chapter 8: 绘图和可视化
基本引入约定
1 | import matplotlib.pyplot as plt |
Figure和Subplot
matplotlib的图像都位于Figure对象中,可以使用plt.figure创建新的Figure
1 | fig = plt.figure() |
Chapter 9: 数据聚合与分组运算
GroupBy技术
例子 [例子-2]
1 | df = pd.DataFrame({'key1': list('aabba'), |
1 | data1 data2 key1 key2 |
按照key1进行分组,计算data1分组后的平均值
1
2
3
4
5
6
7grouped = df['data1'].groupby(df['key1'])
# grouped是groupby的对象
#它实际上还没有进行任何计算,只是含有一些相关分组键df['key']的中间数据
print grouped
# 得到分组后的平均值
print grouped.mean()1
2
3
4
5<pandas.core.groupby.SeriesGroupBy object at 0x00000000078E2DD8>
key1
a 0.283288
b -0.677714
Name: data1, dtype: float64- 数据(Series)根据分组键进行聚合,产生一个新的Series
传入多个数组,会得到不同的结果
1
2
3
4means = df['data1'].groupby([df['key1'], df['key2']]).mean()
print means
print
print means.unstack()1
2
3
4
5
6
7
8
9
10
11key1 key2
a one 0.266446
two 0.316974
b one -0.574834
two -0.780594
Name: data1, dtype: float64
key2 one two
key1
a 0.266446 0.316974
b -0.574834 -0.780594分组键可以是任何长度适当的数组
1
2
3states = np.array(['Ohio', 'California', 'California', 'Ohio', 'Ohio'])
years = np.array([2005,2005, 2006, 2005, 2006])
print df['data1'].groupby([states, years]).mean()1
2
3
4
5California 2005 0.316974
2006 -0.574834
Ohio 2005 -0.481406
2006 0.715109
Name: data1, dtype: float64分组键也可以是列名
1
2
3
4# 因为 key2列不是数值数据,所以从结果中排除了
print df.groupby('key1').mean()
print
print df.groupby(['key1', 'key2']).mean()1
2
3
4
5
6
7
8
9
10
11data1 data2
key1
a 0.283288 0.303794
b -0.677714 -0.125522
data1 data2
key1 key2
a one 0.266446 -0.146678
two 0.316974 1.204739
b one -0.574834 -1.315783
two -0.780594 1.064739GroupBy中 size方法,返回一个含有分组大小的Series
1
print df.groupby(['key1', 'key2']).size()
1
2
3
4
5
6key1 key2
a one 2
two 1
b one 1
two 1
dtype: int64
对分组进行迭代
GroupBy对象支持迭代,可以产生一组二元元组(由分组名和数据块组成)
1
2
3
4
5
6
7
8
9for name, group in df.groupby('key1'):
print name
print group
print "------------分界线----------"
#多重键的情况,元组的第一元素将会有键值组成的元组
for (k1, k2), group in df.groupby(['key1', 'key2']):
print k1, k2
print group1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23a
data1 data2 key1 key2
0 -0.182217 -1.369371 a one
1 0.316974 1.204739 a two
4 0.715109 1.076016 a one
b
data1 data2 key1 key2
2 -0.574834 -1.315783 b one
3 -0.780594 1.064739 b two
------------分界线----------
a one
data1 data2 key1 key2
0 -0.182217 -1.369371 a one
4 0.715109 1.076016 a one
a two
data1 data2 key1 key2
1 0.316974 1.204739 a two
b one
data1 data2 key1 key2
2 -0.574834 -1.315783 b one
b two
data1 data2 key1 key2
3 -0.780594 1.064739 b two可以对这些数据片段做任何操作
将这些数据片段做成一个字典
1
2pieces = dict(list(df.groupby('key1')))
print pieces['b']1
2
3data1 data2 key1 key2
2 -0.574834 -1.315783 b one
3 -0.780594 1.064739 b two
groupby默认在axis = 0 上进行分组,可以在其他任意轴上进行分组
1
2
3
4print df.dtypes
print "----------分割线----------"
grouped = df.groupby(df.dtypes, axis = 1)
print dict(list(grouped))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17data1 float64
data2 float64
key1 object
key2 object
dtype: object
----------分割线----------
{dtype('O'): key1 key2
0 a one
1 a two
2 b one
3 b two
4 a one, dtype('float64'): data1 data2
0 -0.182217 -1.369371
1 0.316974 1.204739
2 -0.574834 -1.315783
3 -0.780594 1.064739
4 0.715109 1.076016}
选取一个或一组列
对于GroupBy对象,用一个或一组列名对其索引,就能实现选取部分进行聚合
1
2df.groupby('key1')['data1'] #等同于
df['data1'].groupby(df['key1'])部分列进行聚合
1
2
3
4
5
6
7
8print df.groupby(['key1', 'key2'])[['data2']].mean()
#分解
#索引操作返回的是一个已经分组的DF或Series
s_grouped = df.groupby(['key1', 'key2'])['data2']
print s_grouped
print s_grouped.mean()1
2
3
4
5
6
7
8
9
10
11
12
13data2
key1 key2
a one -0.146678
two 1.204739
b one -1.315783
two 1.064739
<pandas.core.groupby.SeriesGroupBy object at 0x00000000079156A0>
key1 key2
a one -0.146678
two 1.204739
b one -1.315783
two 1.064739
Name: data2, dtype: float64
通过字典或Series进行分组
除数组以外,分组信息可以是其他形式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19people = pd.DataFrame(np.random.randn(5,5),
columns = list('abcde'),
index = ['Joe', 'Steve', 'Wes','Jim', 'Travis'])
people.ix[2:3, ['b', 'c']] = np.nan #添加几个Na值
print people
print
mapping = {'a': 'red', 'b' : 'red', 'c': 'blue',
'd': 'blue', 'e': 'red', 'f': 'orange'}
#将字典传入GroupBy,会按照值所分组
by_column = people.groupby(mapping, axis = 1)
print by_column.sum()
print
# Series和字典相同
map_series = pd.Series(mapping)
print map_series
print
print people.groupby(map_series, axis = 1).count()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28a b c d e
Joe -2.127568 0.644625 1.527150 -1.723420 1.240916
Steve 0.205717 -0.577552 1.116407 -0.688821 1.786387
Wes 0.441377 NaN NaN 1.937912 -1.081868
Jim 1.229922 -1.001966 -0.582819 -1.197620 -0.644821
Travis 0.724864 0.166980 0.245261 1.648537 -0.074912
blue red
Joe -0.196269 -0.242027
Steve 0.427586 1.414552
Wes 1.937912 -0.640491
Jim -1.780440 -0.416866
Travis 1.893798 0.816932
a red
b red
c blue
d blue
e red
f orange
dtype: object
blue red
Joe 2 3
Steve 2 3
Wes 1 2
Jim 2 3
Travis 2 3
通过函数进行分组
任何被当做分组键的函数都会在各个索引值上被调用一次,返回值被当做分组名称
前面的DF例子,索引值就是人的名字,假设需要按照人名的长度进行分组
1
print people.groupby(len).sum()
1
2
3
4a b c d e
3 3.725168 2.382762 1.475067 2.176910 1.303406
5 -0.337910 0.084666 -0.675073 0.387191 -1.221990
6 0.026837 -1.870793 0.744890 1.697728 0.727134
函数和其他混合使用,任何东西都会转化成数组
1
2key_list = ['one', 'one', 'one', 'two', 'two']
print people.groupby([len, key_list]).min()1
2
3
4
5a b c d e
3 one 1.439995 1.996867 0.052409 0.043291 -0.265731
two -0.140152 0.385895 1.422657 1.269645 -0.051861
5 one -0.337910 0.084666 -0.675073 0.387191 -1.221990
6 two 0.026837 -1.870793 0.744890 1.697728 0.727134
根据索引级别分组
层次化索引数据集可以用索引级别来进行聚合,通过level关键字传入级别编号或者名字
1
2
3
4
5
6
7columns = pd.MultiIndex.from_arrays([['US', "US", 'US', 'JP', 'JP'],
[1,3,5,1,3]], names = ['city','tenor'])
hier_df = pd.DataFrame(np.random.randn(4,5), columns = columns)
print hier_df
print
print hier_df.groupby(level = 'city', axis = 1).count()1
2
3
4
5
6
7
8
9
10
11
12city US JP
tenor 1 3 5 1 3
0 0.033120 0.491344 0.711003 0.107217 0.380337
1 -1.006004 0.288515 -2.025270 -0.046512 1.444702
2 -0.915980 -0.914577 -1.542210 1.667547 0.462239
3 -0.631588 -0.711685 -1.058402 0.351632 -0.374062
city JP US
0 2 3
1 2 3
2 2 3
3 2 3
数据聚合
分组后的聚合处理
用quantile计算样本分位数
1
2
3
4print df
grouped = df.groupby('key1')
print grouped['data1'].quantile(0.9)1
2
3
4
5
6
7
8
9
10data1 data2 key1 key2
0 -1.258749 3.171542 a one
1 -1.284484 1.429326 a two
2 0.229799 -0.981352 b one
3 0.479951 -0.836612 b two
4 0.043948 0.786918 a one
key1
a -0.216591
b 0.454936
Name: data1, dtype: float64使用自己的聚合函数,传入agg或aggregate方法即可
1
2
3
4
5
6
7def peak_to_peak(arr):
return arr.max() - arr.min()
print grouped.agg(peak_to_peak)
print
#有些方法(如describe)也可以使用
print grouped.describe()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23data1 data2
key1
a 1.328433 2.384624
b 0.250152 0.144740
data1 data2
key1
a count 3.000000 3.000000
mean -0.833095 1.795929
std 0.759651 1.233858
min -1.284484 0.786918
25% -1.271617 1.108122
50% -1.258749 1.429326
75% -0.607400 2.300434
max 0.043948 3.171542
b count 2.000000 2.000000
mean 0.354875 -0.908982
std 0.176884 0.102346
min 0.229799 -0.981352
25% 0.292337 -0.945167
50% 0.354875 -0.908982
75% 0.417413 -0.872797
max 0.479951 -0.836612
经过优化的groupby的方法
| 函数 | 说明 |
|---|---|
| count | 分组中非NA值的数量 |
| sum | 非NA值的和 |
| mean | 非NA值的平均值 |
| median | 非NA值的算术中位数 |
| std var | 无偏(分母为n-1)标准差和方差 |
| min max | 非NA值的最小值和最大值 |
| prod | 非NA值的积 |
| first last | 第一个和最后一个非NA值 |
更高级的聚合功能
1 | tips = pd.read_csv('./pydata-book-master/ch08/tips.csv') |
1 | total_bill tip sex smoker day time size tip_pct |
面向列的多函数应用
对于不同列使用不同的聚合函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28#根据sex和smoker对tips进行分组
grouped = tips.groupby(['sex', 'smoker'])
grouped_pct = grouped['tip_pct']
#对于优化的groupby方法可以使用函数名的字符串方式传入
print grouped_pct.agg('mean')
print
#传入一组函数名,则各列或是对应的函数名命名
print grouped_pct.agg(['mean', 'std'])
print
#自定义名字
print grouped_pct.agg([('foo', 'mean'), ('bar', 'std')])
print
#对部分列进行统计
functions = ['count', 'mean', 'max']
result = grouped['tip_pct', 'total_bill'].agg(functions)
print result
print
#对于不同列用不同函数, 传入字典
print grouped.agg({'tip': np.max, 'size': 'sum'})
print
print grouped.agg({'tip': ['max', 'min', 'mean', np.std],
'size': 'sum'})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43sex smoker
Female No 0.156921
Yes 0.182150
Male No 0.160669
Yes 0.152771
Name: tip_pct, dtype: float64
mean std
sex smoker
Female No 0.156921 0.036421
Yes 0.182150 0.071595
Male No 0.160669 0.041849
Yes 0.152771 0.090588
foo bar
sex smoker
Female No 0.156921 0.036421
Yes 0.182150 0.071595
Male No 0.160669 0.041849
Yes 0.152771 0.090588
tip_pct total_bill
count mean max count mean max
sex smoker
Female No 54 0.156921 0.252672 54 18.105185 35.83
Yes 33 0.182150 0.416667 33 17.977879 44.30
Male No 97 0.160669 0.291990 97 19.791237 48.33
Yes 60 0.152771 0.710345 60 22.284500 50.81
tip size
sex smoker
Female No 5.2 140
Yes 6.5 74
Male No 9.0 263
Yes 10.0 150
tip size
max min mean std sum
sex smoker
Female No 5.2 1.00 2.773519 1.128425 140
Yes 6.5 1.00 2.931515 1.219916 74
Male No 9.0 1.25 3.113402 1.489559 263
Yes 10.0 1.00 3.051167 1.500120 150
以”无索引”的形式返回聚合数据
1 | print tips.groupby(['sex', 'smoker']).mean() |
1 | total_bill tip size tip_pct |
分组级运算和转换
聚合只是分组级运算的一种,他是数据转换的一个特例
使用 transform和apply来进行其他分组运算
transform的使用
为DF添加一个存放索引分组平均值的列
聚合后在合并
1
2
3
4
5
6
7
8print df
print
k1_means = df.groupby('key1').mean().add_prefix('mean_')
print k1_means
print
print pd.merge(df, k1_means, left_on = 'key1', right_index = True)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18data1 data2 key1 key2
0 -1.258749 3.171542 a one
1 -1.284484 1.429326 a two
2 0.229799 -0.981352 b one
3 0.479951 -0.836612 b two
4 0.043948 0.786918 a one
mean_data1 mean_data2
key1
a -0.833095 1.795929
b 0.354875 -0.908982
data1 data2 key1 key2 mean_data1 mean_data2
0 -1.258749 3.171542 a one -0.833095 1.795929
1 -1.284484 1.429326 a two -0.833095 1.795929
4 0.043948 0.786918 a one -0.833095 1.795929
2 0.229799 -0.981352 b one 0.354875 -0.908982
3 0.479951 -0.836612 b two 0.354875 -0.908982
在groupby上使用transform
1
2
3
4
5key = ['one', 'two', 'one', 'two', 'one']
print people.groupby(key).mean()
print
print people.groupby(key).transform(np.mean)1
2
3
4
5
6
7
8
9
10a b c d e
one -0.320443 0.405803 0.886206 0.621010 0.028046
two 0.717819 -0.789759 0.266794 -0.943221 0.570783
a b c d e
Joe -0.320443 0.405803 0.886206 0.621010 0.028046
Steve 0.717819 -0.789759 0.266794 -0.943221 0.570783
Wes -0.320443 0.405803 0.886206 0.621010 0.028046
Jim 0.717819 -0.789759 0.266794 -0.943221 0.570783
Travis -0.320443 0.405803 0.886206 0.621010 0.028046transform将一个函数应用于各个分组,然后将结果放到适当的位子
如果各分组产生一个标量值,则该值会被广播出去
列如从各组减去平均值
1
2
3
4
5
6
7
8def demean(arr):
return arr - arr.mean()
demeaned = people.groupby(key).transform(demean)
print demeaned
print
#检测demeaned现在的分组平均值是否是0
print demeaned.groupby(key).mean()1
2
3
4
5
6
7
8
9
10a b c d e
Joe -1.807126 0.238822 0.640945 -2.344429 1.212871
Steve -0.512103 0.212207 0.849613 0.254399 1.215604
Wes 0.761820 NaN NaN 1.316902 -1.109913
Jim 0.512103 -0.212207 -0.849613 -0.254399 -1.215604
Travis 1.045306 -0.238822 -0.640945 1.027527 -0.102958
a b c d e
one 7.401487e-17 2.775558e-17 0.0 -1.480297e-16 -6.476301e-17
two -5.551115e-17 5.551115e-17 0.0 0.000000e+00 0.000000e+00
apply: 一般性的”拆分-应用-合并”
先编写一个选取指定列具有最大值的行的函数
1
2
3def top(df, n = 5, column = 'tip_pct'):
return df.sort_index(by = column)[-n:]
print top(tips, n = 6)1
2
3
4
5
6
7total_bill tip sex smoker day time size tip_pct
109 14.31 4.00 Female Yes Sat Dinner 2 0.279525
183 23.17 6.50 Male Yes Sun Dinner 4 0.280535
232 11.61 3.39 Male No Sat Dinner 2 0.291990
67 3.07 1.00 Female Yes Sat Dinner 1 0.325733
178 9.60 4.00 Female Yes Sun Dinner 2 0.416667
172 7.25 5.15 Male Yes Sun Dinner 2 0.710345如果对smoker分组,并调用appley
分别对分好组的数据进行函数的运算,之后用pd.concat组装在一块
1
print tips.groupby('smoker').apply(top)
1
2
3
4
5
6
7
8
9
10
11
12total_bill tip sex smoker day time size tip_pct
smoker
No 88 24.71 5.85 Male No Thur Lunch 2 0.236746
185 20.69 5.00 Male No Sun Dinner 5 0.241663
51 10.29 2.60 Female No Sun Dinner 2 0.252672
149 7.51 2.00 Male No Thur Lunch 2 0.266312
232 11.61 3.39 Male No Sat Dinner 2 0.291990
Yes 109 14.31 4.00 Female Yes Sat Dinner 2 0.279525
183 23.17 6.50 Male Yes Sun Dinner 4 0.280535
67 3.07 1.00 Female Yes Sat Dinner 1 0.325733
178 9.60 4.00 Female Yes Sun Dinner 2 0.416667
172 7.25 5.15 Male Yes Sun Dinner 2 0.710345如果传给apply的函数能接受其他参数或者关键字,可以在函数名后传入
1
print tips.groupby(['smoker','day']).apply(top, n = 1, column = 'total_bill')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21total_bill tip sex smoker day time size \
smoker day
No Fri 94 22.75 3.25 Female No Fri Dinner 2
Sat 212 48.33 9.00 Male No Sat Dinner 4
Sun 156 48.17 5.00 Male No Sun Dinner 6
Thur 142 41.19 5.00 Male No Thur Lunch 5
Yes Fri 95 40.17 4.73 Male Yes Fri Dinner 4
Sat 170 50.81 10.00 Male Yes Sat Dinner 3
Sun 182 45.35 3.50 Male Yes Sun Dinner 3
Thur 197 43.11 5.00 Female Yes Thur Lunch 4
tip_pct
smoker day
No Fri 94 0.142857
Sat 212 0.186220
Sun 156 0.103799
Thur 142 0.121389
Yes Fri 95 0.117750
Sat 170 0.196812
Sun 182 0.077178
Thur 197 0.115982
禁止分组键
分组键会跟原始对象的索引共同构成层次化索引,可以用group~keys~
= False来禁用该效果1
print tips.groupby('smoker', group_keys = False).apply(top)
1
2
3
4
5
6
7
8
9
10
11total_bill tip sex smoker day time size tip_pct
88 24.71 5.85 Male No Thur Lunch 2 0.236746
185 20.69 5.00 Male No Sun Dinner 5 0.241663
51 10.29 2.60 Female No Sun Dinner 2 0.252672
149 7.51 2.00 Male No Thur Lunch 2 0.266312
232 11.61 3.39 Male No Sat Dinner 2 0.291990
109 14.31 4.00 Female Yes Sat Dinner 2 0.279525
183 23.17 6.50 Male Yes Sun Dinner 4 0.280535
67 3.07 1.00 Female Yes Sat Dinner 1 0.325733
178 9.60 4.00 Female Yes Sun Dinner 2 0.416667
172 7.25 5.15 Male Yes Sun Dinner 2 0.710345
分位数和桶分析
使用拆分工具(cut, qcut)和groupby结合,实现对数据集的 桶或分位数拆分
1
2
3
4
5
6
7
8
9
10frame = pd.DataFrame({'data1': np.random.randn(1000),
'data2': np.random.randn(1000)})
factor = pd.cut(frame.data1, 4)
#由cut返回的Factor对象可用于groupby
def get_stats(group):
return ({'min': group.min(), 'max':group.max(),
'count': group.count(), 'mean': group.mean()})
grouped = frame.data2.groupby(factor)
print grouped.apply(get_stats).unstack()1
2
3
4
5
6count max mean min
data1
(-2.783, -1.0122] 149.0 3.049340 -0.093282 -2.838853
(-1.0122, 0.751] 618.0 3.182495 -0.044764 -2.767363
(0.751, 2.515] 225.0 2.425162 0.094163 -3.482998
(2.515, 4.278] 8.0 0.685021 -0.597498 -1.827032
示例:用特定于分组的值填充缺失值
使用fillna来填充NA值
1 | s = pd.Series(np.random.randn(6)) |
1 | 0 NaN |
示例:随机采样和排列
从一个大数据集中随机抽取样本以进行蒙特卡洛模拟或其他分析
抽取的方法有很多,其中之一就是用np.random.permutation(n)的前k个元素
其中k是期望样本的大小,n为完整数据的大小
得到一套牌组
1
2
3
4
5
6
7
8
9#红桃(hearts), 黑桃(Spades), 梅花(Clubs),方块(Diamonds)
suits = ['H', 'S', 'C', "D"]
card_val = (range(1,11) + [10] * 3) *4
base_names = ['A'] + range(2,11) + ['J', 'Q', 'K']
cards = []
for suit in suits:
cards.extend(str(num) + suit for num in base_names)
deck = pd.Series(card_val, index = cards)
print deck[:14]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15AH 1
2H 2
3H 3
4H 4
5H 5
6H 6
7H 7
8H 8
9H 9
10H 10
JH 10
QH 10
KH 10
AS 1
dtype: int64从整副牌抽取5张
1
2
3def draw(deck, n = 5):
return deck.take(np.random.permutation(len(deck))[:n])
print draw(deck)1
2
3
4
5
6JD 10
3H 3
7S 7
8S 8
5C 5
dtype: int64从各花色中随机抽取两张牌
1
2
3
4
5
6get_suit = lambda card: card[-1] #选取最后一个字母,就是花色
print deck.groupby(get_suit).apply(draw, n = 2)
print
#另一种方法
print deck.groupby(get_suit, group_keys = False).apply(draw, n = 2)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19C 3C 3
10C 10
D 7D 7
5D 5
H 7H 7
2H 2
S 8S 8
7S 7
dtype: int64
KC 10
JC 10
3D 3
7D 7
JH 10
10H 10
JS 10
3S 3
dtype: int64
示例:分组加权平均值和相关系数
根据groupy的”拆分-应用-合并”范式,DF的列与列或两个Series之间的运算
分组加权平均数
加权平均数 是 数据乘以权数的和 / 权数的和
1
2
3
4
5
6
7
8
9
10df = pd.DataFrame({'category': list('aabb'),
'data': np.random.randn(4),
'weights': np.random.rand(4)})
print df
print
#利用category计算分组加权平均数
grouped = df.groupby('category')
get_wavg = lambda g: np.average(g['data'], weights = g['weights'])
print grouped.apply(get_wavg)1
2
3
4
5
6
7
8
9
10category data weights
0 a -0.995153 0.224198
1 a 2.399610 0.921270
2 b 1.248725 0.570782
3 b -1.517198 0.830123
category
a 1.735167
b -0.390256
dtype: float64实际点的例子
来自Yahoo!Finance的数据集,其中含有标准普尔500指数(spx字段)和几只股的收盘价
1
2
3
4close_px = pd.read_csv('./pydata-book-master/ch09/stock_px.csv', parse_dates = True, index_col = 0)
print close_px[-4:]
print
print close_px.pct_change()[-4:]1
2
3
4
5
6
7
8
9
10
11AAPL MSFT XOM SPX
2011-10-11 400.29 27.00 76.27 1195.54
2011-10-12 402.19 26.96 77.16 1207.25
2011-10-13 408.43 27.18 76.37 1203.66
2011-10-14 422.00 27.27 78.11 1224.58
AAPL MSFT XOM SPX
2011-10-11 0.029526 0.002227 -0.000131 0.000544
2011-10-12 0.004747 -0.001481 0.011669 0.009795
2011-10-13 0.015515 0.008160 -0.010238 -0.002974
2011-10-14 0.033225 0.003311 0.022784 0.017380计算由日收益率(通过百分数变化计算)与SPX之间的年度相关数组成的DF
1
2
3
4
5
6
7
8
9
10# pct_change 对比变化,相对上个数字的相对变化
rets = close_px.pct_change().dropna()
spx_corr = lambda x: x.corrwith(x['SPX'])
by_year = rets.groupby(lambda x: x.year)
print by_year.apply(spx_corr)
print
#计算列和列之间的相关关系
#计算苹果和微软的年度相关系数
print by_year.apply(lambda g: g['AAPL'].corr(g['MSFT']))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21AAPL MSFT XOM SPX
2003 0.541124 0.745174 0.661265 1.0
2004 0.374283 0.588531 0.557742 1.0
2005 0.467540 0.562374 0.631010 1.0
2006 0.428267 0.406126 0.518514 1.0
2007 0.508118 0.658770 0.786264 1.0
2008 0.681434 0.804626 0.828303 1.0
2009 0.707103 0.654902 0.797921 1.0
2010 0.710105 0.730118 0.839057 1.0
2011 0.691931 0.800996 0.859975 1.0
2003 0.480868
2004 0.259024
2005 0.300093
2006 0.161735
2007 0.417738
2008 0.611901
2009 0.432738
2010 0.571946
2011 0.581987
dtype: float64
示例: 面向分组的线性回归
1 | #需要安装statsmodels库 |
透视表和交叉表
可以通过groupby以及重塑运算制作透视表
DF有 pivot~table方法~,还有顶级方法pandas.pivot~table函数~
除了能为groupby提供便利之外,pivot~table还可以添加分项小计~(margins)
对小费数据,根据sex和smoker计算分组平均数(默认聚合类型),并将sex和smoker放到行上
1
print tips.pivot_table(index = ['sex', 'smoker'])
1
2
3
4
5
6size tip tip_pct total_bill
sex smoker
Female No 2.592593 2.773519 0.156921 18.105185
Yes 2.242424 2.931515 0.182150 17.977879
Male No 2.711340 3.113402 0.160669 19.791237
Yes 2.500000 3.051167 0.152771 22.284500只想集合tip~pct和size~,根据day进行分组
1
print tips.pivot_table(['tip_pct', 'size'], index = ['sex', 'day'], columns = ['smoker'])
1
2
3
4
5
6
7
8
9
10
11tip_pct size
smoker No Yes No Yes
sex day
Female Fri 0.165296 0.209129 2.500000 2.000000
Sat 0.147993 0.163817 2.307692 2.200000
Sun 0.165710 0.237075 3.071429 2.500000
Thur 0.155971 0.163073 2.480000 2.428571
Male Fri 0.138005 0.144730 2.000000 2.125000
Sat 0.162132 0.139067 2.656250 2.629630
Sun 0.158291 0.173964 2.883721 2.600000
Thur 0.165706 0.164417 2.500000 2.300000传入margins = True添加分项小计
这将会添加标签为ALL的行和列
其值对应于单个等级中所有数据的分组统计
例子中all值为平均值:
- 不单独考虑烟民和非烟民(ALl列)
不单独考虑行分组两个级别中的任意单项(ALL行)
1
2print tips.pivot_table(['tip_pct', 'size'], index = ['sex', 'day'],
columns = 'smoker', margins = True)1
2
3
4
5
6
7
8
9
10
11
12
13tippct size smoker No Yes All No
Yes All sex day Female Fri 0.165296 0.209129
0.199388 2.500000 2.000000 2.111111 Sat 0.147993
0.163817 0.156470 2.307692 2.200000 2.250000 Sun
0.165710 0.237075 0.181569 3.071429 2.500000
2.944444 Thur 0.155971 0.163073 0.157525 2.480000
2.428571 2.468750 Male Fri 0.138005 0.144730
0.143385 2.000000 2.125000 2.100000 Sat 0.162132
0.139067 0.151577 2.656250 2.629630 2.644068 Sun
0.158291 0.173964 0.162344 2.883721 2.600000
2.810345 Thur 0.165706 0.164417 0.165276 2.500000
2.300000 2.433333 All 0.159328 0.163196 0.160803
2.668874 2.408602 2.569672
3. 使用其他聚合函数,将其传入agfunc即可
1
2
3
4
5
6
7
print tips.pivot_table('tip_pct', index = ['sex', 'smoker'], columns = 'day',
aggfunc = len, margins = True)
print
#如果存在NA支,可以设置一个fill_value
print tips.pivot_table('size', index = ['time', 'sex', 'smoker'],
columns = 'day', aggfunc = 'sum', fill_value = 0)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
day Fri Sat Sun Thur All
sex smoker
Female No 2.0 13.0 14.0 25.0 54.0
Yes 7.0 15.0 4.0 7.0 33.0
Male No 2.0 32.0 43.0 20.0 97.0
Yes 8.0 27.0 15.0 10.0 60.0
All 19.0 87.0 76.0 62.0 244.0
day Fri Sat Sun Thur
time sex smoker
Dinner Female No 2 30 43 2
Yes 8 33 10 0
Male No 4 85 124 0
Yes 12 71 39 0
Lunch Female No 3 0 0 60
Yes 6 0 0 17
Male No 0 0 0 50
Yes 5 0 0 23
pivot~table的参数
| 参数名 | 说明 |
|---|---|
| values | 待聚合的列的名称 |
| 默认聚合所有数值列 | |
| index | 用于分组的列名或其他分组键 |
| 出现在结果透视表的行 | |
| columns | 用于分组的列名或者其他分组键 |
| 出现在结果透视表的列 | |
| aggfunc | 聚合函数或函数列表 |
| 默认为’mean’ | |
| 可以使任意对groupby有效的函数 | |
| margins | 添加行/列小计和总计,默认为False |
交叉表
用于计算分组平率的特殊透视表
1
2
3
4
5
6
7
8data = pd.DataFrame({'Sample': range(10),
"Gender": ['Female', 'Male', 'Female', 'Male', 'Male', 'Male', 'Female', 'Female', 'Male', 'Female'],
'Handedness': list('RLRRLRRLRR')})
print data
print
print pd.crosstab(data.Gender, data.Handedness, margins = True)
print1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Gender Handedness Sample
0 Female R 0
1 Male L 1
2 Female R 2
3 Male R 3
4 Male L 4
5 Male R 5
6 Female R 6
7 Female L 7
8 Male R 8
9 Female R 9
Handedness L R All
Gender
Female 1 4 5
Male 2 3 5
All 3 7 10
示例: 2012联邦选取委员会数据库 待补充</span> [示例-2012联邦选取委员会数据库]
Chapter 10: 时间序列
时间序列应用于以下几个主要场景
- 时间戳(timestamp), 特定的时刻
- 固定时间(period),如2007年1月或者2010年全年
- 时间间隔(interval),由起始和结束时间戳表示。时期(period)可以被看做间隔的特例
- 时间或过程时间,每个时间点都是相对于特定起始时间的一个度量
日期和时间数据类型及工具
python拥有 data time 数据类型
我们主要用到datatime time calendar模块
其中datetime.datetime是用的最多的数据类型
1
2
3
4from datetime import datetime
now = datetime.now()
print now
print now.year, now.month, now.day1
22016-09-23 16:40:23.266000
2016 9 23datetime以毫秒形式储存日期和时间,datetime.timedelta表示两个datetime之间的时间差
1
2
3
4delta = datetime(2011, 1, 7) - datetime(2008, 6, 25, 8, 15)
print delta #书上结果是 datetime.timedelta(926,56700)
print delta.days
print delta.seconds1
2
3925 days, 15:45:00
925
56700给datetime对象加上(减去)一个或多个timedelta,会产生新对象
1
2
3
4from datetime import timedelta
start = datetime(2011, 1, 7)
print start + timedelta(12)
print start - 2 * timedelta(12)1
22011-01-19 00:00:00
2010-12-14 00:00:00
datetime模块中的数据类型
| 类型 | 说明 |
|---|---|
| date | 以公历形式储存日历日期(年 月 日) |
| time | 将时间储存为时 分 秒 毫秒 |
| timedelta | 表示两个datetime值之间的查(日, 秒, 毫秒) |
字符串和datetime的相互转换
利用str或strftime方法(传入格式化字符串),datetime和pandas的Timestamp会被转化成字符串
1
2
3stamp = datetime(2011, 1, 3)
print str(stamp)
print stamp.strftime('%Y-%m-%d')1
22011-01-03 00:00:00
2011-01-03datetime.strptime可以用格式化字符将字符串转换成日期
1
2
3
4value = '2011-01-03'
print datetime.strptime(value, '%Y-%m-%d')
datestrs = ['7/6/2011', '8/6/2011']
print [datetime.strptime(x,'%m/%d/%Y') for x in datestrs]1
22011-01-03 00:00:00
[datetime.datetime(2011, 7, 6, 0, 0), datetime.datetime(2011, 8, 6, 0, 0)]datetime.strptime是通过已知格式进行日期解析的最佳方式
但是因为输入格式定义很麻烦,所以可以用dateutil这第三方库的parser.parse方法来处理常见日期格式
1
2
3
4
5
6
7
8from dateutil.parser import parse
print parse('2011-01-03')
#dateutil可以解析几乎所有人类能够理解的日期表示形式
print parse('Jan 31, 1997 10:45 PM')
#国际通用的格式中,日通常出现在月的前面,传入dayfirst = True即可解决这个问题
print parse('6/12/2011', dayfirst = True)pandas通常用于处理成组日期,不管日期是DF的轴索还是列
to~datetime方法解析不同的日期表示~,对标准日期格式(如ISO8601)的解析非常快
1
2
3
4
5
6
7
8
9
10
11print datestrs
print pd.to_datetime(datestrs)
print
#处理缺失值(None, 空字符串等)
idx = pd.to_datetime(datestrs + [None])
print idx
#NaT(not a time)是pandas的时间戳数据的NA值
print idx[2]
print pd.isnull(idx)1
2['7/6/2011', '8/6/2011']
DatetimeIndex(['2011-07-06', '2011-08-06'], dtype='datetime64[ns]', freq=None):
1
2
3DatetimeIndex(['2011-07-06', '2011-08-06', 'NaT'], dtype='datetime64[ns]', freq=None)
NaT
[False False True]datetime格式定义(兼容ISO C89)
| 代码 | 说明 |
|———|————————————————————-|
| %Y | 4位数的年 |
| %y | 2位数的年 |
| %m | 2位数的月[01,12] |
| %d | 2位数的日[01,31] |
| %H | 时(24小时制)[00,23] |
| % | 时(12小时制)[01,12] |
| %M | 2位数的分[00,59] |
| %S | 秒[00,61]秒60 61用于闰秒 |
| %w | 用整数表示的星期几[0(星期天),6] |
| %U | 每年的第几周[00,53] |
| | 星期天被认为每周的第一天 |
| | 每年第一个星期天之前得几天认为是”第0周” |
| %W | 每年的第几周[00,53] |
| | 星期一被认为每周的第一天 |
| | 每年第一个星期一之前得几天认为是 |
| %z | 以+HHMM或-HHMM表示UTC时区偏移量 |
| | 如果时区为naive,返回空字符串 |
| %F | %Y-%m-%d简写形式,列如2012-04-18 |
| %D | %m/%d/%y简写形式,列如04/18/12 |特定于当前环境的日期格式
| 代码 | 说明 |
|———|——————————————————————-|
| %a | 星期几的简写 |
| %A | 星期几的全称 |
| %b | 月份的简写 |
| %B | 月份的全称 |
| %c | 完整的日期和时间 |
| | 列如”Tue 01 May 2012 04:20:57 PM” |
| %p | 不同环境中的AM或PM |
| %x | 适合于不同当前环境的日期格式 |
| | 列如在美国,”May 1, 2012”会产生”05/01/2012” |
| %X | 适合于当前环境的时间格式 |
| | 例如”04:24:12 PM” |
时间序列基础
前提知识
pandas最基本的时间序列类型就是以时间戳为索引的Series
1
2
3
4
5from datetime import datetime
dates = [datetime(2011, 1, 2), datetime(2011, 1, 5), datetime(2011, 1, 7),
datetime(2011, 1, 8), datetime(2011, 1, 10), datetime(2011, 1, 12)]
ts = pd.Series(np.random.randn(6), index = dates)
print ts1
2
3
4
5
6
72011-01-02 -0.845472
2011-01-05 0.326411
2011-01-07 -0.002475
2011-01-08 -0.225972
2011-01-10 0.030098
2011-01-12 1.321111
dtype: float64datetime对象实际上被放在一个DatetimeIndex中
上面的ts就变成一个TimeSeries
1
2
3print type(ts)
print ts.index1
2
3
4<class 'pandas.core.series.Series'>
DatetimeIndex(['2011-01-02', '2011-01-05', '2011-01-07', '2011-01-08',
'2011-01-10', '2011-01-12'],
dtype='datetime64[ns]', freq=None)
不同索引的时间序列之间的算术运算会自动按日期对齐
1
print ts + ts[::2]
1
2
3
4
5
6
72011-01-02 -1.690945
2011-01-05 NaN
2011-01-07 -0.004949
2011-01-08 NaN
2011-01-10 0.060196
2011-01-12 NaN
dtype: float64pandas用numpy的datetime64数据类型以纳秒形式储存时间戳
1
print ts.index.dtype
1
datetime64[ns]
DatetimeIndex的各q个标量值是pandas的Timestamp对象
1
2
3stamp = ts.index[0]
print stamp
print type(stamp)1
22011-01-02 00:00:00
<class 'pandas.tslib.Timestamp'>TimeStamp可以随时自动转换为datetime对象
- 此外还可以储存频率信息(如果有的话),且知道如何执行时区转换及其他操作
索引、选取、子集构造
因为TimeSeries是Series的一个子集,所以在索引和数据选取方面行为是一样的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37stamp = ts.index[2]
print stamp
print ts[stamp]
#另一种更为方便的用法,传入一个可以被解释为日期的字符串
print ts['1/10/2011']
print
print ts['20110110']
print
#对于较长的时间序列,只需传入"年" 或 "年月" 即可获得数据的切片
longer_ts = pd.Series(np.random.randn(1000),
index = pd.date_range('1/1/2000', periods = 1000))
print longer_ts[-5:]
print
print len(longer_ts['2001'])
print
print longer_ts['2001-05']
print
#通过日期进行切片的方式只对规则Series有效
print ts[datetime(2011,1,9):]
print
#对于大部分时间序列是按照时间先后排序的,
#因此可以用不存在于该时间序列中的时间戳来对其切片(范围查询)
print ts['1/6/2011': '1/11/2011'] #可以传入字符串日期,datetime或Timestamp
#另一个截取的方法
print ts.truncate(after = '1/9/2011')
#对DF进行索引
dates = pd.date_range('1/1/2000', periods = 100, freq = 'W-WED') #星期三的日期
long_df = pd.DataFrame(np.random.randn(100,4),
index = dates,
columns = ['Colorado', 'Texas', 'New York', 'Ohio'])
print long_df.ix['5-2001']1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
672011-01-07 00:00:00
-0.00247469657877
0.0300981333535
0.0300981333535
2002-09-22 -1.292983
2002-09-23 -0.663150
2002-09-24 -1.246379
2002-09-25 -0.480229
2002-09-26 -1.425084
Freq: D, dtype: float64
365
2001-05-01 0.522341
2001-05-02 1.105745
2001-05-03 -0.955111
2001-05-04 -0.129135
2001-05-05 -0.480308
2001-05-06 -0.611974
2001-05-07 -0.489040
2001-05-08 1.599516
2001-05-09 -1.718454
2001-05-10 0.896244
2001-05-11 0.398462
2001-05-12 0.114994
2001-05-13 2.601561
2001-05-14 -0.022382
2001-05-15 0.161495
2001-05-16 -0.749117
2001-05-17 0.251396
2001-05-18 0.096229
2001-05-19 0.116899
2001-05-20 0.598400
2001-05-21 -1.143373
2001-05-22 0.740993
2001-05-23 -0.163081
2001-05-24 1.468237
2001-05-25 1.161466
2001-05-26 -0.262837
2001-05-27 0.308709
2001-05-28 -0.332932
2001-05-29 0.536197
2001-05-30 -0.072682
2001-05-31 1.216165
Freq: D, dtype: float64
2011-01-10 0.030098
2011-01-12 1.321111
dtype: float64
2011-01-07 -0.002475
2011-01-08 -0.225972
2011-01-10 0.030098
dtype: float64
2011-01-02 -0.845472
2011-01-05 0.326411
2011-01-07 -0.002475
2011-01-08 -0.225972
dtype: float64
Colorado Texas New York Ohio
2001-05-02 -0.068790 0.991744 -1.583433 -1.133356
2001-05-09 -0.027371 0.003850 -1.567402 -0.744111
2001-05-16 0.920044 -0.274756 -0.112520 -0.577484
2001-05-23 -1.403174 -1.606607 0.509411 0.160882
2001-05-30 1.904117 1.430996 -0.646121 0.874684
带有重复索引的时间序列
例子
1
2
3dates = pd.DatetimeIndex(['1/1/2000', '1/2/2000', '1/2/2000', '1/2/2000','1/3/2000'])
dup_ts = pd.Series(np.arange(5), index = dates)
print dup_ts1
2
3
4
5
62000-01-01 0
2000-01-02 1
2000-01-02 2
2000-01-02 3
2000-01-03 4
dtype: int32检查索引的is~unique属性~,来知道它是不是唯一
1
print dup_ts.index.is_unique
1
False
进行索引,要么标量值,要么切片
1
2
3
4print '不重复的返回值:'
print dup_ts['1/3/2000'] #不重复
print '重复的返回值:'
print dup_ts['1/2/2000'] #重复1
2
3
4
5
6
7不重复的返回值:
4
重复的返回值:
2000-01-02 1
2000-01-02 2
2000-01-02 3
dtype: int32对非唯一的时间戳进行聚合,一个办法是使用groupby,并传入level =
0(索引是唯一一层!)1
2
3
4grouped = dup_ts.groupby(level = 0)
print grouped.mean()
print
print grouped.count()1
2
3
42000-01-01 0
2000-01-02 2
2000-01-03 4
dtype: int32:
1
2
3
42000-01-01 1
2000-01-02 3
2000-01-03 1
dtype: int64
日期的范围、频率以及移动
pandas中的时间序列是不规则的,但是需要频率进行分析时用resample
resample会用缺失值填充没有的地方
1
2
3
4print ts
print
print ts.resample('D').index1
2
3
4
5
6
7
8
9
10
11
122011-01-02 -0.845472
2011-01-05 0.326411
2011-01-07 -0.002475
2011-01-08 -0.225972
2011-01-10 0.030098
2011-01-12 1.321111
dtype: float64
DatetimeIndex(['2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05',
'2011-01-06', '2011-01-07', '2011-01-08', '2011-01-09',
'2011-01-10', '2011-01-11', '2011-01-12'],
dtype='datetime64[ns]', freq='D')
生成日期范围,使用date~range~
1 | index = pd.date_range('4/1/2012','6/1/2012') |
1 | DatetimeIndex(['2012-05-28', '2012-05-29', '2012-05-30', '2012-05-31', |
频率和日期偏移量
基础频率(base
frequency)都由一个字符串表示,比如”M”代表每月,”H”代表每小时对于每一个基础频率,都有一个日期偏移量(date offset)的对象与之对应
列如 小时计算的平率可以用Hour类表示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18from pandas.tseries.offsets import Hour, Minute
hour = Hour()
print hour
#传入一个整数,可定义偏移量的倍数
four_hours = Hour(4)
print four_hours
#一般来说,无需显示创建这样的对象,只需使用诸如"H"和"4H"这样的字符串别名即可
print pd.date_range('1/1/2000', '1/3/2000 23:59', freq = '4h')
print
#大部分偏移量可以用加法来连接
print Hour(2) + Minute(30)
print
#同理,也可以传入频率字符串,这类字符串会被解析为等效的表达式
print pd.date_range('1/1/2000', periods = 6, freq = '1h30min')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<Hour>
<4 * Hours>
DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 04:00:00',
'2000-01-01 08:00:00', '2000-01-01 12:00:00',
'2000-01-01 16:00:00', '2000-01-01 20:00:00',
'2000-01-02 00:00:00', '2000-01-02 04:00:00',
'2000-01-02 08:00:00', '2000-01-02 12:00:00',
'2000-01-02 16:00:00', '2000-01-02 20:00:00',
'2000-01-03 00:00:00', '2000-01-03 04:00:00',
'2000-01-03 08:00:00', '2000-01-03 12:00:00',
'2000-01-03 16:00:00', '2000-01-03 20:00:00'],
dtype='datetime64[ns]', freq='4H')
<150 * Minutes>
DatetimeIndex(['2000-01-01 00:00:00', '2000-01-01 01:30:00',
'2000-01-01 03:00:00', '2000-01-01 04:30:00',
'2000-01-01 06:00:00', '2000-01-01 07:30:00'],
dtype='datetime64[ns]', freq='90T')
有些列如”M”(日历月末), “BM”(每月最后一个工作日)为锚点偏移量(anchored
offset)时间序列的基础频率
| 别名 | 偏移量累性格 | 说明 |
|—————————-|———————————|—————————————————————————-|
| D | Day | 每日历日 |
| B | BusinessDay | 每工作日 |
| H | Hour | 每小时 |
| T或min | Minute | 每分 |
| S | Second | 每秒 |
| L或ms | Milli | 每毫秒(千分之一秒) |
| U | Micro | 每微秒(每百万分之一秒) |
| M | MonthEnd | 每月最后一个日历日 |
| BM | BusinessMonthEnd | 每月最后一个工作日 |
| MS | MonthBegin | 每月第一个日历日 |
| BMS | BusinessMonthBegin | 每月第一个工作日 |
| W-MON W-TUE … | week | 指定的星期几 |
| | | (MON TUE WED THU FIR SAT SUM)开始算起,每周 |
| WOM-1MON WOM-2MON | WeekOfMonth | 产生每月第一, 第二的星期几 |
| | | 列如 WOM-3FRI表示每月第三个星期五 |
| Q-JAN Q-FEB | quarterEnd | 对于以指定月份 |
| | | (JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC) |
| | | 结束的年度,每季度最后一月的最后一个日历日 |
| BQ-JAN | BusinessQuarterEnd | 对于指定月份结束的年度 |
| | | 每季度最后一月的最后一个工作日 |
| QS-JAN | QuarterBegin | 对于以指定月份结束的年度 |
| | | 每季度最后一月的第一个日历日 |
| BQS-JAN | BusinessQuarterBegin | 对于以指定月份结束的年度, |
| | | 每季度最后一月的第一个工作日 |
| A-JAN | YearEnd | 每年指定月份的租后一个日历日 |
| BA-JAN | BusinessYearEnd | 每年指定月份的最后一个工作日 |
| AS-JAN | YearBegin | 每年指定月份的第一个日历日 |
| BAS-JAN | BusinessYearBegin | 每年指定月份的第一个工作日 |WOM日期
WOM(Week of
Month)是一种非常实用的平率类,它以WOM开头,它能使你获得诸如”每月第三个星期五”之类的日期1
2rng = pd.date_range('1/1/2012', '/9/1/2012', freq = 'WOM-3FRI')
print list(rng)1
[Timestamp('2012-01-20 00:00:00', offset='WOM-3FRI'), Timestamp('2012-02-17 00:00:00', offset='WOM-3FRI'), Timestamp('2012-03-16 00:00:00', offset='WOM-3FRI'), Timestamp('2012-04-20 00:00:00', offset='WOM-3FRI'), Timestamp('2012-05-18 00:00:00', offset='WOM-3FRI'), Timestamp('2012-06-15 00:00:00', offset='WOM-3FRI'), Timestamp('2012-07-20 00:00:00', offset='WOM-3FRI'), Timestamp('2012-08-17 00:00:00', offset='WOM-3FRI')]
移动(超前和滞后)数据
移动(shifting)指的是沿着时间轴将数据前移或后移
Series和DataFrame都有一个shift方法用来执行单纯的前移或后移操作,保持索引不变
1
2
3
4
5
6
7
8ts = pd.Series(np.random.randn(4),
index = pd.date_range('1/1/2000', periods = 4, freq = "M"))
print ts
print
print ts.shift(2)
print
print ts.shift(-2)
print1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
172000-01-31 0.008511
2000-02-29 -0.086778
2000-03-31 -0.272538
2000-04-30 0.594061
Freq: M, dtype: float64
2000-01-31 NaN
2000-02-29 NaN
2000-03-31 0.008511
2000-04-30 -0.086778
Freq: M, dtype: float64
2000-01-31 -0.272538
2000-02-29 0.594061
2000-03-31 NaN
2000-04-30 NaN
Freq: M, dtype: float64shift通常用于计算一个或多个时间序列中的百分比变化
列如 ts/ts.shift(1) -1
由于单纯的移位操作不会修改索引,所以部分数据会丢失
如果频率已知,可以将其传给shift以便实现对时间戳进行位移而不是对数据的简单位移
1
2
3
4
5
6
7print ts.shift(2, freq = 'M') #会按频率移动日期,但不移动数据
print
print ts.shift(3, freq = 'D')
print
print ts.shift(1, freq= '3D')
print
print ts.shift(1, freq = '90T')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
232000-03-31 0.008511
2000-04-30 -0.086778
2000-05-31 -0.272538
2000-06-30 0.594061
Freq: M, dtype: float64
2000-02-03 0.008511
2000-03-03 -0.086778
2000-04-03 -0.272538
2000-05-03 0.594061
dtype: float64
2000-02-03 0.008511
2000-03-03 -0.086778
2000-04-03 -0.272538
2000-05-03 0.594061
dtype: float64
2000-01-31 01:30:00 0.008511
2000-02-29 01:30:00 -0.086778
2000-03-31 01:30:00 -0.272538
2000-04-30 01:30:00 0.594061
Freq: M, dtype: float64
通过偏移量对日期进行位移
pandas的日期偏移量可以用于datetime或Timestamp对象上
1
2
3from pandas.tseries.offsets import Day, MonthEnd
now = datetime(2011,11, 17)
print now + 3 * Day()1
2011-11-20 00:00:00
如果加的是锚点偏移量,第一次增量会将原本日期向前滚动到符合频率规则的下个日期
1
2
3print now + MonthEnd() #now日期之后的第一个符合MonthEnd的日期
print
print now + MonthEnd(2)1
2011-11-30 00:00:00
:
1
2011-12-31 00:00:00
通过锚点偏移量的rollforward和rollback方法,可以显示的将日期向前或向后”滚动”
1
2
3
4offset = MonthEnd()
print offset.rollforward(now) #向前增加偏移量类似于 now + offset
print
print offset.rollback(now) #向后增加偏移量 类似于 now - offset1
2011-11-30 00:00:00
:
1
2011-10-31 00:00:00
结合groupby使用 “滚动”方法 没明白</span>
1
2
3
4
5
6ts = pd.Series(np.random.randn(20),
index = pd.date_range('1/15/2000', periods = 20, freq = '4d'))
print ts.groupby(offset.rollforward).mean()
#更简单的和快速的方式是使用resample
print ts.resample('M', how = 'mean')1
2
3
4
5
6
7
82000-01-31 -0.144672
2000-02-29 -0.859409
2000-03-31 0.291670
dtype: float64
2000-01-31 -0.144672
2000-02-29 -0.859409
2000-03-31 0.291670
Freq: M, dtype: float64
时区处理 待补充</span> [时区处理]
时区很多人都选择协调世界时(UTC),时区是以UTC偏移量来表示的
时期及其算术运算
时期(period)表示时间区间
1 | p = pd.Period(2007, freq = 'A-DEC') |
1 | 2007 |
时期的频率转换
Period和PeriodIndex对象都可以通过其asfreq方法被转换成别的频率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#将一个年度时期,转换成当年年初或年末的一个月度时期
p = pd.Period('2007', freq = 'A-DEC')
print p
print
print p.asfreq('M', how = 'start')
print
print p.asfreq('M', how = 'end')
print
#Period("2007", freq = 'A-DEC')看做一个被划分为多个月度时期的时间段中的游标
p = pd.Period('2007', freq = 'A-JUN')
print p.asfreq('M', 'start') #变成结尾日期的一年前的日期了 # 不明白
print
print p.asfreq('M', 'end')
print1
2
3
4
5
6
7
8
92007
2007-01
2007-12
2006-07
2007-06将高频率转换成低频率时,超时期(superperiod)是由子时期(subperiod)所属的未知决定
1
2
3
4
5
6
7
8
9
10
11
12#在A-JUN平率中 月份 "2007年8月"实际上是属于周期"2009年的"
p = pd.Period('2007-08', 'M')
print p.asfreq('A-JUN')
#PeriodIndex或TimeSeries的频率转换方式也是如此
rng = pd.period_range('2006', '2009', freq = 'A-DEC')
ts = pd.Series(np.random.randn(len(rng)), index = rng)
print ts
print
print ts.asfreq('M', how = 'start')
print
print ts.asfreq('B', how = 'end')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
182008
2006 0.035359
2007 -0.089152
2008 -0.334353
2009 -0.935191
Freq: A-DEC, dtype: float64
2006-01 0.035359
2007-01 -0.089152
2008-01 -0.334353
2009-01 -0.935191
Freq: M, dtype: float64
2006-12-29 0.035359
2007-12-31 -0.089152
2008-12-31 -0.334353
2009-12-31 -0.935191
Freq: B, dtype: float64
按季度计算的时期频率
1 | p = pd.Period('2012Q4', freq = 'Q-JAN') #表示p是以1月作为 2012的财年末 就是11月到1月 |
1 | 2012Q4 |
将Timestamp转换成Period(及其反向过程)
通过to~period方法将时间戳索引的Series和DataFrame转换成已时期索引~
1
2
3
4
5
6rng = pd.date_range('1/1/2000', periods = 3, freq = 'M')
ts = pd.Series(np.random.randn(3), index = rng)
pts = ts.to_period()
print ts
print
print pts1
2
3
42000-01-31 1.168085
2000-02-29 0.205205
2000-03-31 1.301836
Freq: M, dtype: float64:
1
2
3
42000-01 1.168085
2000-02 0.205205
2000-03 1.301836
Freq: M, dtype: float64由于时期指的是非重叠时间区间,所以对于给定的频率,一个时间戳只能属于一个时期
新PeriodIndex的频率默认是从时间戳推断出来的,可以指定任何别的频率,结果允许重复时期
1
2
3
4
5
6
7
8
9
10rng = pd.date_range('1/29/2000', periods = 6, freq = 'D')
ts2 = pd.Series(np.random.randn(6), index = rng)
print ts2.to_period('M')
print
#转换为时间戳,使用过to_timestamp
pts = ts.to_period()
print pts
print
print pts.to_timestamp(how = 'end')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
172000-01 0.596077
2000-01 1.005846
2000-01 -0.141036
2000-02 -0.205156
2000-02 1.581535
2000-02 0.146273
Freq: M, dtype: float64
2000-01 1.168085
2000-02 0.205205
2000-03 1.301836
Freq: M, dtype: float64
2000-01-31 1.168085
2000-02-29 0.205205
2000-03-31 1.301836
Freq: M, dtype: float64
通过数组创建Periodindex
固定的数据通常会将时间信息分开存放在多格列
1
2
3
4
5
6data = pd.read_csv('./pydata-book-master/ch08/macrodata.csv')
print data.year[-5:]
print
print data.quarter[-5:]
print
print data.columns1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18198 2008.0
199 2008.0
200 2009.0
201 2009.0
202 2009.0
Name: year, dtype: float64
198 3.0
199 4.0
200 1.0
201 2.0
202 3.0
Name: quarter, dtype: float64
Index([u'year', u'quarter', u'realgdp ', u'realcons', u'realinv', u'realgovt',
u'realdpi', u'cpi', u'm1', u'tbilrate', u'unemp', u'pop', u'infl',
u'realint'],
dtype='object')将多个数组以及一个频率传入PeriodIndex,就可以将他们合并成DF的一个索引
1
2
3
4
5index = pd.PeriodIndex(year = data.year, quarter = data.quarter, freq = 'Q-DEC')
print index[-5:]
print
data.index = index
print data.infl[-5:]1
PeriodIndex(['2008Q3', '2008Q4', '2009Q1', '2009Q2', '2009Q3'], dtype='int64', freq='Q-DEC')
:
1
2
3
4
5
62008Q3 -3.16
2008Q4 -8.79
2009Q1 0.94
2009Q2 3.37
2009Q3 3.56
Freq: Q-DEC, Name: infl, dtype: float64
重采样及频率转换
重采样(resampling)是将时间序列从一个频率转换到另一个频率的处理过程
将高频率数据聚合到低频率称为降采样(downsampling)
将低频率数据转换到高频率称为升采样(upsampling)
pandas对象都有一个resample方法,它是各种频率转换工作的主要函数
1 | rng = pd.date_range('1/1/2000', periods = 100, freq = 'D') |
1 | 2000-01-31 0.246297 |
resample方法的参数
| 参数 | 说明 |
|---|---|
| freq | 表示重采样频率的字符串或DateOffset |
| 列如’M’ ‘5min’或 Second(15) | |
| how = ‘mean’ | 用于产生聚合值的函数名或数组函数 |
| 列如 ‘mean’ ‘ohlc’ ‘np.max’等 | |
| 默认为’mean’ | |
| 其他常用的: first, last median ohlc max min | |
| axis = 0 | 重采样的轴,默认为axis = 0 |
| fill~method~ = None | 盛彩阳时如何插值,比如’ffill’ ‘bfill’ |
| 默认为不差值 | |
| closed = ‘right’ | 在降采样中,各时间段的哪一端是闭合(包含) |
| ‘right’或 ‘left’, 默认为’left’ | |
| label = ‘right’ | 在降采样中,如何设置聚合值的标签 |
| ‘right’或 ‘left’(面元的右边界或左边界) | |
| 列如9:30到9:35之间的这5分钟被标记为9:30或9:35 | |
| 默认为”left” | |
| loffset = None | 面元标签的时间校正值 |
| 比如’-1s’/Second(-1)用于将聚合标签调早1秒 | |
| limit = None | 再向前或向后填充时,允许填充的最大时间数 |
| kind = None | 聚合到时期(“period”)或时间戳(“timestamp”) |
| 默认聚合到时间序列的索引类型 | |
| convention= None | 当重采样时期时,将低频率转换到高频率所用的约定 |
| (“sart”或”end”),默认为”end” |
降采样
带聚合的数据不必拥有固定的频率,期望的频率会自动定义聚合的面元边界
需要考虑的两样东西
- 各区间哪边是闭合的
如何标记各个聚合面元,用区间的开头还是末尾
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20rng = pd.date_range('1/1/2000', periods = 12, freq = 'T')
ts = pd.Series(np.arange(12), index = rng)
print ts
print
#如果想要将这些一分钟的数据聚合到5分钟的块里, 默认是left
print ts.resample('5min', how = 'sum', closed = 'right')
print
print ts.resample('5min', how = 'sum')
print
#label 默认为left
print ts.resample('5min', how = 'sum', label = 'right')
print
#对数据做些位移, 比如通过减去1秒来更容易明白时间戳表示哪个区间
print ts.resample('5min', how = 'sum', loffset = '-1s')
#此外可可用用结果对象的shift方法来位移#+begin~example~ 2000-01-01 00:00:00 0 2000-01-01 00:01:00 1
2000-01-01 00:02:00 2 2000-01-01 00:03:00 3 2000-01-01 00:04:00
4 2000-01-01 00:05:00 5 2000-01-01 00:06:00 6 2000-01-01
00:07:00 7 2000-01-01 00:08:00 8 2000-01-01 00:09:00 9
2000-01-01 00:10:00 10 2000-01-01 00:11:00 11 Freq: T, dtype:
int321999-12-31 23:55:00 0 2000-01-01 00:00:00 15 2000-01-01 00:05:00
40 2000-01-01 00:10:00 11 Freq: 5T, dtype: int322000-01-01 00:00:00 10 2000-01-01 00:05:00 35 2000-01-01
00:10:00 21 Freq: 5T, dtype: int322000-01-01 00:05:00 10 2000-01-01 00:10:00 35 2000-01-01
00:15:00 21 Freq: 5T, dtype: int321999-12-31 23:59:59 10 2000-01-01 00:04:59 35 2000-01-01
00:09:59 21 Freq: 5T, dtype: int32
#+end~example~
OHLC重采样
金融领域中有一个时间序列局和方式,即计算各面元的四个值:
- 第一个值:开盘(open)
- 最后一个值: 收盘(close)
- 最大值: 最高(high)
- 最小值 最低(low)
传入 how = ‘ohlc’即可得到
1
print ts.resample('5min', how = 'ohlc')
1
2
3
4open high low close
2000-01-01 00:00:00 0 4 0 4
2000-01-01 00:05:00 5 9 5 9
2000-01-01 00:10:00 10 11 10 11
通过groupby进行重采样
1
2
3
4
5
6rng = pd.date_range('1/1/2000', periods = 100, freq = 'D')
ts = pd.Series(np.arange(100), index = rng)
print ts.groupby(lambda x :x.month).mean()
print
print ts.groupby(lambda x: x.weekday).mean()1
2
3
4
5
6
7
8
9
10
11
12
13
141 15
2 45
3 75
4 95
dtype: int32
0 47.5
1 48.5
2 49.5
3 50.5
4 51.5
5 49.0
6 50.0
dtype: float64
升采样和插值
1 | frame = pd.DataFrame(np.random.randn(2,4), |
1 | Colorado Texas New York Ohio |
通过实践进行重采样
1 | frame = pd.DataFrame(np.random.randn(24,4), |
1 | Colorado Texas New York Ohio |
由于时期指的是时间区间,所以升采样和降采样的规则就比较严格
在降采样中,目标频率必须是源频率的子时期(subperiod)
在升采样中,目标频率必须是源频率的超时期(superperiod)
如果不满足以上两条,则会发生异常
主要影响的是按季, 年 周计算的频率
列如 Q-MAR定义的时间区间只能升采样为A-MAR, A-JUM, A-SEP,
A-DEC等1
print annual_frame.resample('Q-MAR', fill_method = 'ffill')
1
2
3
4
5
6
7
8
9Colorado Texas New York Ohio
2000Q4 0.289860 0.022104 -0.468915 0.008858
2001Q1 0.289860 0.022104 -0.468915 0.008858
2001Q2 0.289860 0.022104 -0.468915 0.008858
2001Q3 0.289860 0.022104 -0.468915 0.008858
2001Q4 -0.068266 0.454458 -0.011052 0.074813
2002Q1 -0.068266 0.454458 -0.011052 0.074813
2002Q2 -0.068266 0.454458 -0.011052 0.074813
2002Q3 -0.068266 0.454458 -0.011052 0.074813
时间序列绘图 待补充</span> [时间序列绘图]
移动窗口函数 待补充</span> [移动窗口函数]
性能和内存使用方面的注意事项
Timestamp和Period都是以64位整数表示的
性能方面pandas对数据对齐,和重采样运算进行了高度优化
chapter 11: 金融和经济数据应用
本章术语
截面(cross-section) 表示某个时间点的数据
- 例如 标准普尔500指数中所有成分股在特定日期的收盘价就形成了一个截面
面板(panel) 表示多个时间点的截面数据
多个数据项(列如价格和成交量)在多个时间点的截面数据就构成了一个面板
面板数据可以被表示为层次化索引的DataFrame, 也可以表示为三维的Panel
pandas对象
数据规整化方面的话题
时间序列以及截面对齐
处理金融数据中,最费神的就是所谓的”数据对齐”(data alignment)问题
主要的问题
- 两个相关的时间序列的索引没有很好的对齐
- 两个DataFrame对象可能含有不匹配的列或行
pandas在算术运算中自动对齐数据
例子数据
1
2
3
4
5
6
7
8
9
10
11all_prices = pd.read_csv('./pydata-book-master/ch11/stock_px.csv',
index_col = 0, parse_dates = True)
all_volume = pd.read_csv('./pydata-book-master/ch11/volume.csv',
index_col = 0, parse_dates = True)
prices = all_prices[['AAPL', 'JNJ', 'SPX', 'XOM']]
prices = prices.ix[pd.date_range('9/6/2011 00:00:00','9/14/2011 00:00:00')].dropna()
print prices
volume = all_volumne[['AAPL', 'JNJ', 'XOM']]
volume = volumne.ix[pd.date_range('9/6/2011', '9/12/2011')].dropna()
print volume1
2
3
4
5
6
7
8
9
10
11
12
13
14AAPL JNJ SPX XOM
2011-09-06 379.74 64.64 1165.24 71.15
2011-09-07 383.93 65.43 1198.62 73.65
2011-09-08 384.14 64.95 1185.90 72.82
2011-09-09 377.48 63.64 1154.23 71.01
2011-09-12 379.94 63.59 1162.27 71.84
2011-09-13 384.62 63.61 1172.87 71.65
2011-09-14 389.30 63.73 1188.68 72.64
AAPL JNJ XOM
2011-09-06 18173500.0 15848300.0 25416300.0
2011-09-07 12492000.0 10759700.0 23108400.0
2011-09-08 14839800.0 15551500.0 22434800.0
2011-09-09 20171900.0 17008200.0 27969100.0
2011-09-12 16697300.0 13448200.0 26205800.0用有效数据计算一个成交加权平均价格(假设成交量是价格的子集)
1
2
3
4
5
6
7
8
9print prices * volume
print
vwap = (prices * volume).sum() /volume.sum()
print vwap
print
#因为SPX在volume中没有,所以丢弃
print vwap.dropna()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19AAPL JNJ SPX XOM
2011-09-06 6.901205e+09 1.024434e+09 NaN 1.808370e+09
2011-09-07 4.796054e+09 7.040072e+08 NaN 1.701934e+09
2011-09-08 5.700561e+09 1.010070e+09 NaN 1.633702e+09
2011-09-09 7.614489e+09 1.082402e+09 NaN 1.986086e+09
2011-09-12 6.343972e+09 8.551710e+08 NaN 1.882625e+09
2011-09-13 NaN NaN NaN NaN
2011-09-14 NaN NaN NaN NaN
AAPL 380.655181
JNJ 64.394769
SPX NaN
XOM 72.024288
dtype: float64
AAPL 380.655181
JNJ 64.394769
XOM 72.024288
dtype: float64如果需要手工对齐,使用DF的align方法,
它返回一个元组,含有两个对象的重索引版本1
print prices.align(volume, join = 'inner')
1
2
3
4
5
6
7
8
9
10
11( AAPL JNJ XOM
2011-09-06 379.74 64.64 71.15
2011-09-07 383.93 65.43 73.65
2011-09-08 384.14 64.95 72.82
2011-09-09 377.48 63.64 71.01
2011-09-12 379.94 63.59 71.84, AAPL JNJ XOM
2011-09-06 18173500.0 15848300.0 25416300.0
2011-09-07 12492000.0 10759700.0 23108400.0
2011-09-08 14839800.0 15551500.0 22434800.0
2011-09-09 20171900.0 17008200.0 27969100.0
2011-09-12 16697300.0 13448200.0 26205800.0)通过一组索引可能不同的Series构建一个DF
1
2
3
4
5
6
7
8
9
10
11
12
13s1 = pd.Series(range(3), index = list('abc'))
s2 = pd.Series(range(4), index = list('dbce'))
s3 = pd.Series(range(3), index = list('fac'))
print pd.DataFrame({'one': s1,
'two': s2,
'three': s3})
print
#显示定义结果索引(丢弃其余的数据)
print pd.DataFrame({'one': s1,
'two': s2,
'three': s3},
index = list('face'))1
2
3
4
5
6
7
8
9
10
11
12
13one three two
a 0.0 1.0 NaN
b 1.0 NaN 1.0
c 2.0 2.0 2.0
d NaN NaN 0.0
e NaN NaN 3.0
f NaN 0.0 NaN
one three two
f NaN 0.0 NaN
a 0.0 1.0 NaN
c 2.0 2.0 2.0
e NaN NaN 3.0
频率不同的时间序列的运算
因为盈利预测调整随时可能发生等原因,频率转换和冲对齐的两个主要方法是resample和reindex
resample用于将数据转换到固定频率,reindex用于使数据符合一个新索引
这两方法都支持插值逻辑
周型时间序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25ts1 = pd.Series(np.random.randn(3),
index = pd.date_range('2012-6-13', periods = 3, freq = 'W-WED'))
print ts1
print
#将其重采样到工作日频率,没有的数据会是NAN
print ts1.resample('B')
print
#填充空白,处理较低频率数据常常这么干
print ts1.resample('B', fill_method = 'ffill')
print
#在实际工作中,将较低频率的数据升采样到较高的规整频率是一个不错的解决方案
#但是对于更一般化的不规整时间序列不太适合
dates = pd.DatetimeIndex(['2012-6-12', '2012-6-17', '2012-6-18',
'2012-6-21', '2012-6-22', '2012-6-29'])
ts2 = pd.Series(np.random.randn(6), index = dates)
print ts2
print
#在维持ts2的日期索引,处理ts1和ts2之间的运算,用reindex是更好的解决方法
print ts1.reindex(ts2.index, method = 'ffill')
print
print ts2 + ts1.reindex(ts2.index, method = 'ffill')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
432012-06-13 1.644655
2012-06-20 -1.245813
2012-06-27 1.016858
Freq: W-WED, dtype: float64
DatetimeIndexResampler [freq=<BusinessDay>, axis=0, closed=left, label=left, convention=start, base=0]
2012-06-13 1.644655
2012-06-14 1.644655
2012-06-15 1.644655
2012-06-18 1.644655
2012-06-19 1.644655
2012-06-20 -1.245813
2012-06-21 -1.245813
2012-06-22 -1.245813
2012-06-25 -1.245813
2012-06-26 -1.245813
2012-06-27 1.016858
Freq: B, dtype: float64
2012-06-12 -0.068556
2012-06-17 -0.537801
2012-06-18 0.171733
2012-06-21 -0.187693
2012-06-22 -1.213072
2012-06-29 -0.092329
dtype: float64
2012-06-12 NaN
2012-06-17 1.644655
2012-06-18 1.644655
2012-06-21 -1.245813
2012-06-22 -1.245813
2012-06-29 1.016858
dtype: float64
2012-06-12 NaN
2012-06-17 1.106854
2012-06-18 1.816387
2012-06-21 -1.433506
2012-06-22 -2.458885
2012-06-29 0.924529
dtype: float64
使用period
period提供了另一种处理不同频率时间序列的方法,尤其对特殊规范的以年或季度为频率的金融或经济序列
比如一个公司可能会发布6月结尾的财年的每季度盈利报告,其频率为Q-JUN
1
2
3
4
5
6
7
8gdp = pd.Series([1.78, 1.94, 2.08, 2.01, 2.15, 2.31, 2.46],
index = pd.period_range('1984Q2', periods = 7, freq = 'Q-SEP'))
infl = pd.Series([0.025, 0.045, 0.037, 0.04],
index = pd.period_range('1982', periods = 4, freq = 'A-DEC'))
print gdp
print
print infl
print1
2
3
4
5
6
7
8
9
10
11
12
13
141984Q2 1.78
1984Q3 1.94
1984Q4 2.08
1985Q1 2.01
1985Q2 2.15
1985Q3 2.31
1985Q4 2.46
Freq: Q-SEP, dtype: float64
1982 0.025
1983 0.045
1984 0.037
1985 0.040
Freq: A-DEC, dtype: float64跟Timestamp的时间序列不同,Period索引的两个不同频率之间的运算必须进行显示换算
已知infl值是每年年末观察的
1
2
3
4
5infl_q = infl.asfreq('Q-SEP', how = 'end')
print infl_q
#然后这个时间序列重索引
print infl_q.reindex(gdp.index, method = 'ffill')1
2
3
4
5
6
7
8
9
10
11
12
131983Q1 0.025
1984Q1 0.045
1985Q1 0.037
1986Q1 0.040
Freq: Q-SEP, dtype: float64
1984Q2 0.045
1984Q3 0.045
1984Q4 0.045
1985Q1 0.037
1985Q2 0.037
1985Q3 0.037
1985Q4 0.037
Freq: Q-SEP, dtype: float64
时间和”最当前”数据选取
假设有一个很长的盘中市场数据时间序列,希望抽取其中每天特定时间的价格数据
但是如果数据不规则(观察值没有精确的落在期望的时间点上)
如果不够小心,很容易导致错误的数据规整化
例子数据
1
2
3
4
5
6
7
8#生成一个交易日内的日期范围和时间序列
rng = pd.date_range("2012-06-01 09:30", '2012-06-01 15:59', freq = 'T')
#生成5天的时间点(9:30到15:59之间的值)
rng = rng.append([rng + pd.offsets.BDay(i) for i in range(1,4)])
ts = pd.Series(np.arange(len(rng), dtype = float), index = rng)
print ts[:5]
print ts[-5:]1
2
3
4
5
6
7
8
9
10
11
122012-06-01 09:30:00 0.0
2012-06-01 09:31:00 1.0
2012-06-01 09:32:00 2.0
2012-06-01 09:33:00 3.0
2012-06-01 09:34:00 4.0
dtype: float64
2012-06-06 15:55:00 1555.0
2012-06-06 15:56:00 1556.0
2012-06-06 15:57:00 1557.0
2012-06-06 15:58:00 1558.0
2012-06-06 15:59:00 1559.0
dtype: float64利用python的datetime.time对象进行索引即可抽取这样的时间点上的值
1
2
3
4
5
6from datetime import time
print ts[time(10,0)]
print
#实际上,该操作使用了实例方法at_time
print ts.at_time(time(10,0))1
2
3
4
5
6
7
8
9
10
112012-06-01 10:00:00 30.0
2012-06-04 10:00:00 420.0
2012-06-05 10:00:00 810.0
2012-06-06 10:00:00 1200.0
dtype: float64
2012-06-01 10:00:00 30.0
2012-06-04 10:00:00 420.0
2012-06-05 10:00:00 810.0
2012-06-06 10:00:00 1200.0
dtype: float64between~time方法~,用于选取两个time对象之间的值
1
print ts.between_time(time(10,0), time(10,1))
1
2
3
4
5
6
7
8
92012-06-01 10:00:00 30.0
2012-06-01 10:01:00 31.0
2012-06-04 10:00:00 420.0
2012-06-04 10:01:00 421.0
2012-06-05 10:00:00 810.0
2012-06-05 10:01:00 811.0
2012-06-06 10:00:00 1200.0
2012-06-06 10:01:00 1201.0
dtype: float64对于刚好没有所对应的值(上午10点),需要得到之前(上午10点之前)最后出现的值
1
2
3
4
5
6
7
8
9indexer = np. sort(np.random.permutation(len(ts))[700:])
irr_ts = ts.copy()
irr_ts[indexer] = np.nan
print irr_ts['2012-06-01 09:50': '2012-06-01 10:00']
print
#将一组TImestamp传入asof方法,能得到最近的有效值
selection = pd.date_range('2012-06-01 10:00', periods = 4, freq = 'B')
print irr_ts.asof(selection)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
182012-06-01 09:50:00 NaN
2012-06-01 09:51:00 NaN
2012-06-01 09:52:00 22.0
2012-06-01 09:53:00 NaN
2012-06-01 09:54:00 NaN
2012-06-01 09:55:00 25.0
2012-06-01 09:56:00 NaN
2012-06-01 09:57:00 NaN
2012-06-01 09:58:00 NaN
2012-06-01 09:59:00 29.0
2012-06-01 10:00:00 NaN
dtype: float64
2012-06-01 10:00:00 29.0
2012-06-04 10:00:00 420.0
2012-06-05 10:00:00 809.0
2012-06-06 10:00:00 1200.0
Freq: B, dtype: float64
拼接多个数据源
- 在一个特定的时间点上,从一个数据源切换到另一个数据源
- 用另一个时间序列对当前时间序列中的缺失值”打补丁”
- 将数据中的符号(国家 资产代码等)替换成实际数据
在特定时刻从一个时间序列切换到另一个,就是用pandas.concat将两个TimeSeries或DF合并
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18data1 = pd.DataFrame(np.ones((6,3), dtype = float),
columns = ['a', 'b', 'c'],
index = pd.date_range('6/12/2012', periods = 6))
data2 = pd.DataFrame(np.ones((6,3), dtype = float) * 2,
columns = ['a', 'b', 'c'],
index = pd.date_range('6/13/2012', periods = 6))
spliced = pd.concat([data1.ix[:'2012-06-14'], data2.ix['2012-06-15':]])
print spliced
print
#假如data1缺失了data2中存在的某个时间序列
data2 = pd.DataFrame(np.ones((6,4), dtype = float) * 2,
columns = list('abcd'),
index = pd.date_range('6/13/2012', periods = 6))
spliced = pd.concat([data1.ix[:'2012-06-14'], data2.ix['2012-06-15':]])
print spliced1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17a b c
2012-06-12 1.0 1.0 1.0
2012-06-13 1.0 1.0 1.0
2012-06-14 1.0 1.0 1.0
2012-06-15 2.0 2.0 2.0
2012-06-16 2.0 2.0 2.0
2012-06-17 2.0 2.0 2.0
2012-06-18 2.0 2.0 2.0
a b c d
2012-06-12 1.0 1.0 1.0 NaN
2012-06-13 1.0 1.0 1.0 NaN
2012-06-14 1.0 1.0 1.0 NaN
2012-06-15 2.0 2.0 2.0 2.0
2012-06-16 2.0 2.0 2.0 2.0
2012-06-17 2.0 2.0 2.0 2.0
2012-06-18 2.0 2.0 2.0 2.0combine~first可以引入合并点之前的数据~,这样就扩展了’d’项的历史
1
2
3
4spliced_filled = spliced.combine_first(data2)
print spliced_filled
#如果data2没有关于2012-06-12的数据,所以也就没有值被填充到那一天1
2
3
4
5
6
7
8a b c d
2012-06-12 1.0 1.0 1.0 NaN
2012-06-13 1.0 1.0 1.0 2.0
2012-06-14 1.0 1.0 1.0 2.0
2012-06-15 2.0 2.0 2.0 2.0
2012-06-16 2.0 2.0 2.0 2.0
2012-06-17 2.0 2.0 2.0 2.0
2012-06-18 2.0 2.0 2.0 2.0DF也有一个类似的方法update,可以实现就地更新,如果只想填充空洞,必须传入
overwrite = False1
2spliced.update(data2, overwrite = False)
print spliced1
2
3
4
5
6
7
8
9#+RESULTS:
: a b c d
: 2012-06-12 1.0 1.0 1.0 NaN
: 2012-06-13 1.0 1.0 1.0 2.0
: 2012-06-14 1.0 1.0 1.0 2.0
: 2012-06-15 2.0 2.0 2.0 2.0
: 2012-06-16 2.0 2.0 2.0 2.0
: 2012-06-17 2.0 2.0 2.0 2.0
: 2012-06-18 2.0 2.0 2.0 2.0利用DF的索引机制直接多列进行设置
1
2
3cp_spliced = spliced.copy()
cp_spliced[['a', 'c']] = data1[['a', 'c']]
print cp_spliced1
2
3
4
5
6
7
8
9#+RESULTS:
: a b c d
: 2012-06-12 1.0 1.0 1.0 NaN
: 2012-06-13 1.0 1.0 1.0 2.0
: 2012-06-14 1.0 1.0 1.0 2.0
: 2012-06-15 1.0 2.0 1.0 2.0
: 2012-06-16 1.0 2.0 1.0 2.0
: 2012-06-17 1.0 2.0 1.0 2.0
: 2012-06-18 NaN 2.0 NaN 2.0
收益指数和累计收益
收益(return)通常是指某资产价格的百分比变化
使用2011到2012年苹果股票价格数据来为例
1
2
3
4
5
6
7import pandas.io.data as web
#price = web.get_data_yahoo('AAPL', '2011-01-01')['Adj Close']
price = web.get_data_yahoo('AAPL', '2011-01-01', '2012-07-27')['Adj Close']
print price[-5:]
#计算两点之间的累计百分比回报,只需计算价格的百分比即可
print price['2011-10-03'] / price['2011-3-01'] - 11
2
3
4
5
6
7
8
9#+RESULTS:
: Date
: 2012-07-23 78.974527
: 2012-07-24 78.593927
: 2012-07-25 75.199948
: 2012-07-26 75.188178
: 2012-07-27 76.532688
: Name: Adj Close, dtype: float64
: 0.0723998875455计算某只股票上赚了多少,通常都会先计算一个收益指数
收益指数为一个表示单位投资(比如1美元)收益的时间序列
从收益指数中可以得出很多假设,列如,人们可以决定是否进行利润在投资
可以利用cumprod计算一个简单的收益指数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19returns = price.pct_change() #转化成百分比
ret_index = (1 + returns).cumprod()
ret_index[0] = 1 #将第一个值设置为1,因为元数据是NAN值
print ret_index[:5]
print ret_index[-5:]
print
#计算指定时期内的累积收益
m_returns = ret_index.resample('BM', how = 'last').pct_change()
print m_returns['2012']
print
#使用重采样聚合(聚合为时期)从日百分比变化中计算
m_rets = (1 + returns).resample('M', how = 'prod', kind = 'period') - 1
print m_rets['2012']
#如果知道了股息的派发日和支付率,就可以将他们计入每日总收益
#returns[dividend_dates]+= dividend_pcts1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34Date
2011-01-03 1.000000
2011-01-04 1.005219
2011-01-05 1.013442
2011-01-06 1.012622
2011-01-07 1.019874
Name: Adj Close, dtype: float64
Date
2012-07-23 1.832175
2012-07-24 1.823345
2012-07-25 1.744607
2012-07-26 1.744334
2012-07-27 1.775526
Name: Adj Close, dtype: float64
Date
2012-01-31 0.127111
2012-02-29 0.188311
2012-03-30 0.105283
2012-04-30 -0.025970
2012-05-31 -0.010702
2012-06-29 0.010853
2012-07-31 0.001986
Freq: BM, Name: Adj Close, dtype: float64
Date
2012-01 0.127111
2012-02 0.188311
2012-03 0.105283
2012-04 -0.025970
2012-05 -0.010702
2012-06 0.010853
2012-07 0.001986
Freq: M, Name: Adj Close, dtype: float64
分组变换和分析
以一组假象的股票投资组合为例
1 | #随机生成1000个股票代码 |
1 | Momentum ShortInterest Value |
要对这些按行业分组的投资组合进行各种变换,可以编写自定义的变换函数
列如行业内标准处理,它广泛用于股票资产投资组合的构建过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def zscore(group):
return (group - group.mean()) / group.std()
df_stand = by_industry.apply(zscore)
print df_stand.groupby(industries).agg(['mean', 'std']) #处理过后,各行业的平均值为0, 标准差为1
print
#内置变换函数(如rank)的用法会更简洁一些
# 行业内降序排名
ind_rank = by_industry.rank(ascending = False)
print ind_rank.groupby(industries).agg(['min', 'max'])
print
#通过将rank和zscore链接在一起即可完成整个变换过程
#行业内排名和标准化
print by_industry.apply(lambda x : zscore(x.rank()))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76Momentum ShortInterest Value
mean std mean std mean std
industry
FINANCIAL 6.047277e-16 1.0 2.853175e-15 1.0 -1.365673e-16 1.0
TECH -1.950589e-15 1.0 5.267482e-16 1.0 1.656866e-14 1.0
Momentum ShortInterest Value
min max min max min max
industry
FINANCIAL 1.0 226.0 1.0 226.0 1.0 226.0
TECH 1.0 274.0 1.0 274.0 1.0 274.0
Momentum ShortInterest Value
VTKGN 0.114706 0.558234 0.481764
KUHMP -1.552350 0.879410 -0.084117
XNHTQ -0.451175 -1.674702 -1.231174
GXZVX 1.001763 0.068823 1.506467
ISXRM 0.527646 0.588822 -0.313529
CLPXZ 1.705290 1.644114 -1.384115
MWGUO -0.170366 0.548957 -0.422760
ASKVR 0.206470 1.460585 0.772351
AMWGI -1.644114 0.344117 -1.429997
WEOGZ -0.485858 -0.233464 -0.738252
ULCIN -1.353526 1.598232 -1.017057
YCOSO 0.927548 0.372281 -0.599436
VOZPP 0.094648 -0.018930 -0.687774
LPKOH -0.894704 -0.818234 1.062939
EEPRM -1.646871 -1.104224 -0.662534
CTWYV -0.599436 -1.003266 -0.460619
XYOKS 0.022941 -0.160588 -0.527646
HVWXP 1.231174 1.659408 -0.282941
YPLRZ 1.066365 1.280899 -1.457575
XUCPM -0.347042 -0.637295 -1.545913
QVGTD -0.971174 1.689996 1.154703
FUIVC -0.145127 -0.309183 -1.078984
DSBOX -0.588822 0.099412 -0.971174
NRAQP 1.521761 0.405293 1.368821
OKJZA 1.338232 -0.879410 0.542940
AYEDF 0.700393 -0.561577 1.243040
UYALC -1.445291 -1.506467 -1.460585
GFQJE 1.292350 0.313529 -0.802939
NBCZF 1.003266 1.520674 1.407096
JTVXE -0.435380 -0.296563 -1.343998
... ... ... ...
XLKXN 1.255660 0.334422 -0.624675
TEBLI -0.619410 -1.628820 1.353526
NIWTJ 0.282941 0.649999 0.497058
DSODA -0.788731 -1.684730 -0.384901
QNOKM 1.205181 -1.571153 1.457575
KCNDR 1.432336 0.410140 1.495434
TEFGP 0.848822 1.001763 -1.277056
KXONF -0.965407 1.306139 -0.574196
CYCDF -0.713013 -0.599436 -0.182986
OQLKQ -0.296563 -0.056789 -0.725633
GUQPA 0.665293 -1.689996 -1.261762
ITFQZ -0.750872 0.750872 -1.432336
LNFAG 0.056789 1.078984 -0.990646
MPVHI 0.498478 1.205181 0.372281
SRSWC 0.940586 0.084117 -0.986468
DMPRF -1.179942 -0.157746 -1.381857
DXGAH 0.344117 -1.613526 0.741763
NQTDN 0.182986 -0.271324 -0.044169
ISJVE 0.082028 -0.170366 1.053745
ZNLSJ 0.466470 1.108821 -1.537055
ALHRN -0.374705 -1.307644 -0.130000
VMEDO -1.571153 -0.069408 -0.813971
GFYDX 0.940168 -1.116843 -0.334422
NYVHM -1.091604 -0.006310 -1.293519
CJYCG -0.573528 -0.864116 -0.894704
BWRPM -0.056789 -1.659490 0.309183
GPSUE -1.047645 -1.644114 1.429997
QRPSN -0.772351 -1.338232 -0.359411
YQQAD 1.318759 -0.574196 1.015886
PTDQE -0.328823 -0.711175 0.955880
[500 rows x 3 columns]
分组因子暴露
因子分析(factor analysis)是投资组合定量管理中的一种技术
投资组合的持有量和性能(收益和损失)可以被分解为一个或多个表示投资组合权重的因子(风险因子就是其一)
列如,某只股票的价格与某个基准(比如标准普尔500指数)的协动性被称作贝塔风险系数
下面以一个人为构成的投资组合为例,它由三个随机生成的因子(因子载荷)和一些权重构成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28from numpy.random import rand
fac1, fac2, fac3 = np.random.rand(3, 1000)
ticker_subset = tickers.take(np.random.permutation(N)[:1000])
#因子加权和以及噪声
port = pd.Series(0.7 * fac1 - 1.2 * fac2 + 0.3 * fac3 + rand(1000),
index = ticker_subset)
factors = pd.DataFrame({'f1': fac1, 'f2': fac2, 'f3': fac3},
index = ticker_subset)
#各因子与投资组合之间的矢量相关性
print factors.corrwith(port)
print
#计算因子暴露的标准方法是最小二乘回归, 使用pandas.ols(将factors作为解释变量)
#j计算出整个投资组合的暴露
print pd.ols(y = port, x = factors).beta
print
#因为没有给投资组合添加过多的随机噪声,所以原始的因子权重基本可以恢复,还可以通过groupby计算各行业的暴露量
#为了达到这个目的,先编写一个函数
def beta_exposure(chunk, factors = None):
return pd.ols(y = chunk, x = factors).beta
#根据行业进行分组,并应用该函数,传入因子载荷的dataFrame
by_ind = port.groupby(industries)
exposures = by_ind.apply(beta_exposure, factors = factors)
print exposures.unstack()
十分位和四分位分析
Chapter 12: NUMPY高级应用
ndarray对象的内部机理
ndarray内部由以下内容组成
- 一个指向数组(一个系统内存块)的指针
- 数据类型或dtype
- 一个表示数组形状(shanpe)的元组就是shape属性
一个跨度元组(stride),其中的整数指的是为了前进到当前维度下一个元素需要”跨过”的字节数
- 列如一个典型的(C顺序)3 X 4 X
5的float64(8个字节)数组,其跨度为(160, 40, 8) 其中8为 8字节, 40为 8 * 5, 160为 8 * 5 * 4
1
print np.ones((3,4,5),dtype = np.float64).strides
1
(160L, 40L, 8L)
- 列如一个典型的(C顺序)3 X 4 X
Numpy数据类型体系
检查数组中所包含的是否是整数、浮点数、字符串或python对象
dtype有一个超类(np.integer
np.floating)他们可以跟np.issubdtype函数结合使用1
2
3
4ints = np.ones(10, dtype = np.uint16)
floats = np.ones(10, dtype = np.float32)
print np.issubdtype(ints.dtype, np.integer)
print np.issubdtype(floats.dtype, np.floating)1
2
3#+RESULTS:
: True
: True
高级数组操作
数组重塑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30arr = np.arange(8)
print arr
print
print arr.reshape(4,2)
print
#多重数组也能被重塑
print arr.reshape(4,2).reshape(2,4)
print
#作为参数的形状的其中一维可以使-1,它代表该维度的大小由数据本身推断而来
arr = np.arange(15)
print arr.reshape(5,-1)
print
#由于数组的shape属性是一个元组,因此它也可以传入reshape
other_arr = np.ones((3,5))
print other_arr
print
print arr.reshape(other_arr.shape)
print
#与reshape将一维数组转换为多维数组的运算相反的运算称为扁平化(flattening)或散开(raveling)
arr = np.arange(15).reshape(5,3)
print arr
print
print arr.ravel() #ravel不会产生元数据的副本
print
print arr.flatten() #flatten的行为类似于ravel,但是它总是返回数据的副本1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33[0 1 2 3 4 5 6 7]
[[0 1]
[2 3]
[4 5]
[6 7]]
[[0 1 2 3]
[4 5 6 7]]
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]
[12 13 14]]
[[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]
[ 1. 1. 1. 1. 1.]]
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]]
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]
[12 13 14]]
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14]C和Fortran顺序
行和列优先顺序分别称为C和Fortran顺序
reshape和reval这样的函数,都可以接受一个表示数组数据存放顺序的order参数
一般可以使 “C” “F”等
1
2
3
4
5
6arr = np.arange(12).rshape((3,4))
print arr
print arr.ravel()
print
print arr.ravel('F')
print
二维或更高维数的重塑过程比较难,C和Fortran顺序的关键区别就是维度的行进顺序
- C/行优先顺序:先经过更高的维度(列如,轴1会先于轴0被处理)
- Fortran/列优先顺序:
后经过更高对的维度(列如,轴0会优先轴1被处理)
数组的合并和拆分
numpy.concatenate可以按指定轴将一个数组组成的序列连接到一起
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24arr1 = np.array([[1,2,3], [4,5,6]])
arr2 = np.array([[7,8,9], [10, 11, 12]])
print np.concatenate([arr1, arr2], axis = 0)
print
print np.concatenate([arr1, arr2], axis = 1)
print
#对于常见的连接操作,NumPy提供了一些比较方便的方法(vstack hstack)
print np.vstack((arr1, arr2))
print
print np.hstack((arr1, arr2))
print
#split则将一个数组沿着指定轴拆分为多个数组
from numpy.random import randn
arr = randn(5,2)
print arr
first, second, third = np.split(arr, [1,3])
print first
print
print second
print
print third
print1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]]
[[ 1 2 3]
[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[[ 1 2 3 7 8 9]
[ 4 5 6 10 11 12]]
[[-0.23461861 1.40157661]
[-0.7095222 1.58978999]
[ 0.79123076 -0.21607249]
[-0.37967201 0.66574423]
[ 1.188882 0.36979158]]
[[-0.23461861 1.40157661]]
[[-0.7095222 1.58978999]
[ 0.79123076 -0.21607249]]
[[-0.37967201 0.66574423]
[ 1.188882 0.36979158]]数组 连接函数
| 函数 | 说明 |
|———————————|————————————————————————-|
| concatenate | 最一般化的连接,沿一条轴连接一组数组 |
| vstack, row~stack~ | 以面向行的方式对数组进行堆叠(沿轴0) |
| hstack | 以面向列的方式对数组进行堆叠(沿轴1) |
| column~stak~ | 类似于hstack,但是会先将一维数组转化成二维列向量 |
| dstack | 以面向”深度”的方式对数据进行堆叠(沿轴2) |
| split | 沿指定轴在指定的位置拆分数组 |
| hsplit vsplit dsplit | split的便捷函数,分别沿轴0,1,2进行拆分 |堆叠辅助类r~和c~_
NumPy命名空间中有两个特殊的对象 r~和~
c_,他们可以使数组的堆叠操作更为简洁1
2
3
4
5
6
7arr = np.arange(6)
arr1 = arr.reshape((3,2))
arr2 = randn(3,2)
print np.r_[arr1, arr2]
print
print np.c_[np.r_[arr1, arr2], arr]
print1
2
3
4
5
6
7
8
9
10
11
12
13[[ 0. 1. ]
[ 2. 3. ]
[ 4. 5. ]
[-1.28377882 0.02789502]
[-0.3805641 2.72583374]
[ 0.53381564 -1.28980722]]
[[ 0. 1. 0. ]
[ 2. 3. 1. ]
[ 4. 5. 2. ]
[-1.28377882 0.02789502 3. ]
[-0.3805641 2.72583374 4. ]
[ 0.53381564 -1.28980722 5. ]]
元素的重复操作: tile和repeat
repeat会将数组中的各个元素重复一定次数,从而产生一个更大的数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29arr = np.arange(3)
print arr.repeat(3)
print
#默认传入一个整数,则是各元素重复的次数,如果传入一组整数,则表示各元素可以重复的不同次数
print arr.repeat([2,3,4])
print
arr = randn(2,2)
print arr
print
print arr.repeat(2, axis = 0) #注意,如果没有设置轴向,则数组会扁平化
print
#在多维进行重复时,可以传入整数,对各切片重复不同次数
print arr.repeat([2,3], axis = 0)
print
print arr.repeat([2,3], axis = 1)
print
#tile的功能是沿特定轴向堆叠数组的副本,可以想象成"铺瓷砖"
print arr
print
print np.tile(arr, 2) #第二个参数是数量,对于标量,瓷砖是水平铺设的,而不是垂直
#它可以是一个表示"铺设"布局的元组
print arr
print
print np.tile(arr, (2,1))
print
print np.tile(arr, (3,2))
print1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40[0 0 0 1 1 1 2 2 2]
[0 0 1 1 1 2 2 2 2]
[[-1.37933387 -0.06097814]
[-2.41454401 -1.80084267]]
[[-1.37933387 -0.06097814]
[-1.37933387 -0.06097814]
[-2.41454401 -1.80084267]
[-2.41454401 -1.80084267]]
[[-1.37933387 -0.06097814]
[-1.37933387 -0.06097814]
[-2.41454401 -1.80084267]
[-2.41454401 -1.80084267]
[-2.41454401 -1.80084267]]
[[-1.37933387 -1.37933387 -0.06097814 -0.06097814 -0.06097814]
[-2.41454401 -2.41454401 -1.80084267 -1.80084267 -1.80084267]]
[[-1.37933387 -0.06097814]
[-2.41454401 -1.80084267]]
[[-1.37933387 -0.06097814 -1.37933387 -0.06097814]
[-2.41454401 -1.80084267 -2.41454401 -1.80084267]]
[[-1.37933387 -0.06097814]
[-2.41454401 -1.80084267]]
[[-1.37933387 -0.06097814]
[-2.41454401 -1.80084267]
[-1.37933387 -0.06097814]
[-2.41454401 -1.80084267]]
[[-1.37933387 -0.06097814 -1.37933387 -0.06097814]
[-2.41454401 -1.80084267 -2.41454401 -1.80084267]
[-1.37933387 -0.06097814 -1.37933387 -0.06097814]
[-2.41454401 -1.80084267 -2.41454401 -1.80084267]
[-1.37933387 -0.06097814 -1.37933387 -0.06097814]
[-2.41454401 -1.80084267 -2.41454401 -1.80084267]]
花式索引的等价函数: take和put
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21arr = np.arange(10) * 100
inds = [7,1,2,6]
print arr[inds]
print
#ndarray有两个方法专门用于获取和设置单个轴向的选区
print arr.take(inds)
arr.put(inds, 42)
print arr
print
arr.put(inds, [40, 41, 42, 43])
print arr
print
#要在其他轴上使用take,只需传入axis关键字即可
inds = [2, 0, 2, 1]
arr = randn(2, 4)
print arr
print
print arr.take(inds, axis = 1)1
2
3
4
5
6
7
8
9
10
11
12[700 100 200 600]
[700 100 200 600]
[ 0 42 42 300 400 500 42 42 800 900]
[ 0 41 42 300 400 500 43 40 800 900]
[[-0.03295171 -0.33494885 0.15989473 -0.7112217 ]
[-0.09916891 0.05100988 0.22046994 -0.28954971]]
[[ 0.15989473 -0.03295171 0.15989473 -0.33494885]
[ 0.22046994 -0.09916891 0.22046994 0.05100988]]- put不接受axis参数,它只会在数据的扁平化版本(一维,
C顺序)上进行索引(可能会改善)
- put不接受axis参数,它只会在数据的扁平化版本(一维,
广播
广播(broadcasting)是不同形状的数组之间的运算的执行方式
将标量和数组合并时,就会发生最简单的广播
1 | arr = np.arange(5) |
1 | [0 1 2 3 4] |
广播的原则
如果两个数组的后缘维度(trailing
dimension,从末尾开始算起的维度)的轴长度相符或其中一方的长度为1,则认为他们是广播兼容的广播会在缺失和(或)长度为1的维度进行
aixs参数
axis区别图 就是假设n x
m的df,计算时,aixs=0表示的是n,计算行之间的,所以是跨行
1 | import numpy as np |
an(axis= 0) #跨行
print ‘’
print df.mean(axis = 1) #跨列
print ‘’
print df.drop(0,axis=1) #丢掉列中的0列
```