Python金融大数据分析——第4章 数据类型和结构 笔记

  • 第4章 数据类型和结构
    • 4.1 基本数据类型
      • 4.1.1 整数
      • 4.1.2 浮点数
      • 4.1.3 字符串
    • 4.2 基本数据结构
      • 4.2.1 元组
      • 4.2.2 列表
      • 4.2.3 离题:控制结构
      • 4.2.4 离题:函数式编程
      • 4.2.5 字典
      • 4.2.6 集合
    • 4.3 NumPy数据结构
      • 4.3.1 用 Pyhon 列表形成数组
      • 4.3.2 常规NumPy数组
      • 4.3.3 结构数组
    • 4.4 代码向量化
      • 4.4.1 基本向量化
    • 4.5 内存布局

第4章 数据类型和结构

4.1 基本数据类型

4.1.1 整数

a = 10
type(a)  # int
# 调用 blt_length 方法, 获得表现 int 对象所需的位数
a.bit_length()  # 4
a = 100000
# 对象所赋的整数值越大, 需要的位数越多
a.bit_length()  # 17
googol = 10 ** 100
googol.bit_length()  # 333

4.1.2 浮点数

type(1 / 4)  # float
b = 0.35
type(b)  # float
b + 0.1  # 0.44999999999999996

出现以上结果的原因是浮点数在内部以二进制形式表:也就是说. 十进制数 n ( O

c = 0.5
c.as_integer_ratio()  # (1, 2)

as_integer_ratio()将一个float用分数表示出来,返回的是一个二元元组
0.5可以精确保存. 因为它具备精确(有限)的二进制表示:0.5=1/2。但是. b=0.35和预期的实数0.35=7/20不同:

b.as_integer_ratio()  # (3152519739159347, 9007199254740992)

精度取决于表示数值所用的位数。 一般说, Python运行的所有平台使用IEEE 754双精度标准( 64 位)作为内部表示。这相当于 15 位数字的相对精度。由于这个主题在多个金融应用领域中都很重要。有时候必须确保数值的精确(至少尽可能达到最佳)。例如,在加总一组数量很多的数值时, 这个问题就可能很重要。在这种情况下。某个种类或者幅度的表示误差可能汇聚起,造成和基准值的显著偏差。
decimal 模块为浮点数提供了任意精确度的对象,以及使用这些数值时处理精度问题的多个选项:

import decimal
from decimal import Decimal

decimal.getcontext()
# context(prec:2a, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999,999, capitals=l, flags=[], traps=[Overflow, InvalidOperation, DivisionsyZero])
d = Decimal(1) / Decimal(11)  # Decimal('0.09090909090909090909090909091')

decimal.getcontext().prec = 4  # lower precision tha.n default
e = Decimal(1) / Decimal(11)  # Decimal('0.09091')
decimal.getcontext().prec = 50  # higher precision than default
f = Decimal(1) / Decimal(11)  # Decimal('0.090909090909090909090909090909090909090909090909091')

g = d + e + f  # Decimal('0.27272818181818181818181818181909090909090909090909')

4.1.3 字符串

t = 'this is a string object'
t.capitalize()  # 'This is a string object'
t.split()  # ['this', 'is', 'a', 'string', 'object']
t.find('string')  # 10
t.find('Python')  # -1
t.replace(' ', '|')  # 'this|is|a|string|object'
'http://www.python.org'.strip('htp:/')  # 'www.python.org'

精选字符串方法

方法 参数 返回/结果
cpitalize () 复制字符串,将第一个字符改成大写
count sub[,star[,end]]) 计算子字符串出现的次数
decode [encoding[,errors]]) 用encoding指定的编码方式(例如UTF-8)解码字将串
encode ([encoding[,errors]]) 字符串编码形式
find (sub[,start[,end]]) 找到的子字符串(最低)索引
join (seq) 连接seq序列中的字符串
replace (old,new[,count]) 用new替换前count个old
split ([sep[,maxsplit]]) 字符串中的单词列表,以sep作为分隔符
splitlines ([keepends]) 如果keepends为真,以行结束符/换行符分隔的行
strip (chars) 从字符串首/尾删除chars中的字符
upper () 复制字符串 , 所有字母政为大写

正则表达式

import re

series = """
'01/18/2014 13:00:00',100,'1st;
'01/18/2014 13:30:00',110,'snd;
'01/18/2014 14:00:00',120,'3rd;
"""
dt = re.compile("'[0-9/:\s]+'")  # 正则两边的单引号必须有,不然匹配的结果都散了
result = dt.findall(series)
# ["'01/18/2014 13:00:00'", "'01/18/2014 13:30:00'", "'01/18/2014 14:00:00'"]

from datetime import datetime

pydt = datetime.strptime(result[0].replace("'", ""), '%m/%d/%Y %H:%M:%S')
print(pydt)  # 2014-01-18 13:00:00

4.2 基本数据结构

4.2.1 元组

元组(tple)是一种高级的数据结构,但是其应用仍然相当简单有限, 通过在圆括号

t = (1, 2.5, 'data')
type(t)  # tuple
t = 1, 2.5, 'data'  # 可以去掉括号
type(t)  # tuple
# Python使用零起点编号,元组的第3个元素在索引位置2上
t[2]  # 'data'
type(t[2])  # str
t.count('data')  # 1
t.index('data')  # 2

4.2.2 列表

与元组对象相比,列表(list)类型的对象更灵活、更强大。从金融角度看,许多工作只能用列表对象完成,例如存储股票报价和附加新数据。列表对象通过方括号定义。基本功能和行为与元组类似:

l = [1, 2.5, 'data']
l.append([4, 3])  # [1, 2.5, 'data', [4, 3]]
l.extend([4, 3])  # [1, 2.5, 'data', [4, 3], 4, 3]
l.insert(1, 'insert')  # [1, 'insert', 2.5, 'data', [4, 3], 4, 3]
l.append('data')  # [1, 'insert', 2.5, 'data', [4, 3], 4, 3, 'data']
l.remove('data')  # [1, 'insert', 2.5, [4, 3], 4, 3, 'data'] 移除了第一个匹配的,如果不存在会报错
p = l.pop(3)
print(l, p)  # [1, 'insert', 2.5, 4, 3, 'data'] [4, 3]
del l[0]  # ['insert', 2.5, 4, 3, 'data']

切片操作也很容易实现。这里的切片(Slicing)指的是将数据集分解为(感兴趣的)较小部分:

l = [1, 'insert', 2.5, 1.0, 1.5, 2.0]
l[2:5]  # 3rd to 5th elements ['data', [4, 3], 4]
# [2.5, 1.0, 1.5]

精选的列表对象操作和方法

方法 参数 返回/结果
l[i]=x [i] 用x代替第i个元素
l[i:j:k]=s [i:j:k] 用s代替从i到j-1号元素中的每第k个元素
append (x) 在对象后附加x
count (x) 对象x出现的次数
del l[i:j:k] [i:j:k] 删除索引值从i到j-1号元素的每第k个元素
extend (s) 将s的所有元素附加到对象
index (x[,i[,j]]) 元素i和j-1之间第一个x的索引
insert (i,x)++ 在索引i之前插入x
remove (i) 删除索引为i的元素
pop (i) 删除索引为i的元素并返回
reverse () 颠倒所有项目的顺序
sort (cmp,[,key[,reverse]]) 排序所有项目

4.2.3 离题:控制结构

l = [1, 'insert', 2.5, 1.0, 1.5, 2.0]
for element in l[2:5]:
    print(element ** 2)
# 6.25
# 1.0
# 2.25

r = range(0, 8, 1)  # start, end, step width
type(r)  # range
list(r)  # [0, 1, 2, 3, 4, 5, 6, 7]

for i in range(2, 5):
    print(l[i] ** 2)
# 6.25
# 1.0
# 2.25

for i in range(1, 10):
    if i % 2 == 0:
        print('%d is even' % i)
    elif i % 3 == 0:
        print('%d is multiple of 3' % i)
    else:
        print('%d is odd' % i)
# 1 is odd
# 2 is even
# 3 is multiple of 3
# 4 is even
# 5 is odd
# 6 is even
# 7 is odd
# 8 is even
# 9 is multiple of 3

total = 0
while total < 100:
    total += 1
print(total)  # 100

m = [i ** 2 for i in range(5)]  # [0, 1, 4, 9, 16]

4.2.4 离题:函数式编程

Python也提供一些用于函数编程支持的工具一一即在一整组输入(在我们的例子中是列表对象)上应用某个函数。这些工具是过滤(filter)、映射(map)和归纳(reduce )。但是, 我们首先需要个函数定义。从简单的功能出发, 考虑返回输入 x 的平方数的函数 f:

def f(x):
    return x ** 2


f(2)  # 4


def even(x):
    return x % 2 == 0


even(3)  # False
m = map(even, range(10))
list(m)  # [True, False, True, False, True, False, True, False, True, False]
m = map(lambda x: x ** 2, range(10))
list(m)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
f = filter(even, range(15))
list(f)  # [0, 2, 4, 6, 8, 10, 12, 14]

# 在Python 3里,reduce()函数已经被从全局名字空间里移除了,它现在被放置在fucntools模块里用的话要 先引入
# from functools import reduce
from functools import reduce

reduce(lambda x, y: x + y, range(10))  # 45


# 下面是非函数式实现:
def cumsum(l):
    total = 0
    for elem in l:
        total += elem
    return total


cumsum(range(10))  # 45

4.2.5 字典

字典(dict)对象就是可以按照键码(可能是字符串对象)读取的数据字典 , 也是一种可变序列. 是所谓的籍道存储。 列表对象是有序且可排序的. 而字典对象是元序 、 不可排序的。 示例最能够说明和列表对象的不同之处。 花括号是字典对象定义:

d = {
    'Name': 'Angela Merkel',
    'Country': 'Germany',
    'Profession': 'Chancelor',
    'Age': 60
}
type(d)  # dict
print(d['Name'], d['Age'])  # Angela Merkel 60
d.items()
# dict_items([('Name', 'Angela Merkel'), ('Country', 'Germany'), ('Profession', 'Chancelor'), ('Age', 60)])
birthday = True
if birthday is True:
    d['Age'] += 1
print(d['Age'])  # 61

for item in d.items():
    print(item)
# ('Name', 'Angela Merkel')
# ('Country', 'Germany')
# ('Profession', 'Chancelor')
# ('Age', 61)

for value in d.values():
    print(type(value))
# <class 'str'>
# <class 'str'>
# <class 'str'>
# <class 'int'>

字典对象精选操作和方法

方法 参数 返回/结果
d[k] [k] d中键码为k的项目
d[k]=x [k] 将键码为k的项目设置为x
del d[k] [k] 删除键码为k的项目
clear () 删除所有项目
copy () 创建一个拷贝
has_key (k) 如果k是一个键码,为真
items () 所有键-值对的拷贝
keys () 所有键码的拷贝
popitem (k) 返回并删除键码为k的项目
update ([e]) 用来自e的项目更新字典项目
values () 所有值得拷贝

4.2.6 集合

这种对象是其他对象的无序集合, 每个元素只包含一次:

s = set(['u', 'd', 'ud', 'du', 'd', 'du'])  # {'d', 'du', 'u', 'ud'}
t = set(['d', 'dd', 'uu', 'u'])
s.union(t)  # all of s and t {'d', 'dd', 'du', 'u', 'ud', 'uu'}
s.intersection(t)  # both in s and t  {'d', 'u'}
s.difference(t)  # in s but not t  {'du', 'ud'}
t.difference(s)  # {'dd', 'uu'}
s.symmetric_difference(t)  # in either one but not both  {'dd', 'du', 'ud', 'uu'}

from random import randint

l = [randint(0, 10) for i in range(1000)]
l[:20]  # [6, 5, 4, 10, 1, 1, 6, 0, 2, 0, 5, 3, 3, 4, 6, 7, 2, 0, 3, 2]
s = set(l)
s  # {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

4.3 NumPy数据结构

4.3.1 用 Pyhon 列表形成数组

v = [0.5, 0, 75, 1, 0, 1.5, 2.0]  # vector of numbers
m = [v, v, v]  # matrix of numbers
# [[0.5, 0, 75, 1, 0, 1.5, 2.0],
#  [0.5, 0, 75, 1, 0, 1.5, 2.0],
#  [0.5, 0, 75, 1, 0, 1.5, 2.0]]
m[1]  # [0.5, 0, 75, 1, 0, 1.5, 2.0]
m[1][0]  # 0.5

v1 = [0.5, 1.5]
v2 = [1, 2]
m = [v1, v2]  # [[0.5, 1.5], [1, 2]]
c = [m, m]  # cube of numbers  [[[0.5, 1.5], [1, 2]], [[0.5, 1.5], [1, 2]]]
c[1][1][0]  # 1

v = [0.5, 0, 75, 1, 0, 1.5, 2.0]  # vector of numbers
m = [v, v, v]  # matrix of numbers
# [[0.5, 0, 75, 1, 0, 1.5, 2.0],
#  [0.5, 0, 75, 1, 0, 1.5, 2.0],
#  [0.5, 0, 75, 1, 0, 1.5, 2.0]]
v[0] = 'Python'
m
# [['Python', 0, 75, 1, 0, 1.5, 2.0],
#  ['Python', 0, 75, 1, 0, 1.5, 2.0],
#  ['Python', 0, 75, 1, 0, 1.5, 2.0]]
# 使用copy模块的deepcopy函数可以避免这一现象
from copy import deepcopy

v = [0.5, 0.75, 1.0, 1.5, 2.0]
m = 3 * [deepcopy(v)]
m
# [[0.5, 0.75, 1.0, 1.5, 2.0],
#  [0.5, 0.75, 1.0, 1.5, 2.0],
#  [0.5, 0.75, 1.0, 1.5, 2.0]]

v[0] = 'Python'
m
# [[0.5, 0.75, 1.0, 1.5, 2.0],
#  [0.5, 0.75, 1.0, 1.5, 2.0],
#  [0.5, 0.75, 1.0, 1.5, 2.0]]

4.3.2 常规NumPy数组

import numpy as np

a = np.array([0, 0.5, 1.0, 1.5, 2.0])
type(a)  # numpy.ndarray
a[:2]  # indexing as with list objects in 1 dimension
# array([ 0. ,  0.5])

a.sum()  # sum of all elements
# 5.0
a.std()  # standard deviation
# 0.70710678118654757
a.cumsum()  # running cumulative sum
# array([ e. , 0.5, 1.5, 3. , 5. ])
a * 2
# array([ 0.,  1.,  2.,  3.,  4.])
a ** 2
# array([ 0.  ,  0.25,  1.  ,  2.25,  4.  ])
np.sqrt(a)
# array([ 0.        ,  0.70710678,  1.        ,  1.22474487,  1.41421356])

b = np.array([a, a * 2])
# array([[ 0. ,  0.5,  1. ,  1.5,  2. ],
#        [ 0. ,  1. ,  2. ,  3. ,  4. ]])
b[0]
# array([ 0. ,  0.5,  1. ,  1.5,  2. ])

b[0, 2]  # third element of first row
# 1.0
b.sum()
# 15.0

b.sum(axis=0)
# sum along axis 0, i.e. column-wise sum
# array([ 0. ,  1.5,  3. ,  4.5,  6. ])

b.sum(axis=1)
# sum along axis 11 i.e. row-wise sum
# array([  5.,  10.])

c = np.zeros((2, 3, 4), dtype='i', order='C')
# array([[[0, 0, 0, 0],
#         [0, 0, 0, 0],
#         [0, 0, 0, 0]],
#        [[0, 0, 0, 0],
#         [0, 0, 0, 0],
#         [0, 0, 0, 0]]], dtype=int32)

d = np.ones_like(c, dtype='float16', order='C')
# array([[[ 1.,  1.,  1.,  1.],
#         [ 1.,  1.,  1.,  1.],
#         [ 1.,  1.,  1.,  1.]],
#        [[ 1.,  1.,  1.,  1.],
#         [ 1.,  1.,  1.,  1.],
#         [ 1.,  1.,  1.,  1.]]], dtype=float16)

Numpy dtype 对象

dtype 描述 示例
t 位域 t4(4位)
b 布尔值 b(true 或者 false)
I 整数 i8(64位)
u 无符号整数 u8(64位)
f 浮点数 f8(64位)
c 浮点负数 c16(128位)
o 对象 O(指向对象的指针)
S,a 字符串 S24(24个字符串)
U Unicode U24(24个Unicode字符)
V 其他 V12(12字节数据块)

练习

import random

I = 5000
% time mat = [[random.gauss(0, 1) for j in range(I)] for i in range(I)]
# Wall time: 35.4 s

from functools import reduce
% time reduce(lambda x, y: x + y, \
       [reduce(lambda x, y: x + y, row) \
        for row in mat])
# Wall time: 3.48 s
import numpy as np
% time mat = np.random.standard_normal((I, I))
# Wall time: 1.13 s

%time mat.sum()
# Wall time: 88 ms

4.3.3 结构数组

dt = np.dtype([('Name', 'S10'), ('Age', 'i4'), ('Height', 'f'), ('Children/Pets', 'i4', 2)])
s = np.array([('Smith', 45, 1.83, (0, 1)),
              ('Jones', 53, 1.72, (2, 2))], dtype=dt)
s
# array([(b'Smith', 45,  1.83000004, [0, 1]),
#        (b'Jones', 53,  1.72000003, [2, 2])],
#       dtype=[('Name', 'S10'), ('Age', '), ('Height', '), ('Children/Pets', ', (2,))])

s['Name']
# array([b'Smith', b'Jones'],
#       dtype='|S10')
s['Height'].mean()
# 1.7750001
s[1]['Age']
# 53

4.4 代码向量化

代码的向量化是获得执行速度更快、更紧凑代码的一种策略。 基本思路是 “一次 ” 在一个复杂对象上进行操作,或者向其应用某个函数, 而不是通过在对象的单个元素上循环来进行。在Python中,函数式编程工具map、 filter 和 reduce 提供了向量化的手段。在某种意义上,NumPy的向最化探植于其核心之中。

4.4.1 基本向量化

r = np.random.standard_normal((4, 3))
r
# array([[-0.86142195, -1.10576948, -1.72895607],
#        [-0.52694343,  0.885586  ,  2.09117311],
#        [ 1.17578779, -0.81735752, -0.79021266],
#        [-0.16469963,  2.31507942,  0.17910542]])

s = np.random.standard_normal((4, 3))
s
# array([[-0.40514995,  0.21406042, -0.89967644],
#        [ 1.98133139, -2.29530214,  0.23899185],
#        [-1.5984521 , -0.37488275,  1.52037602],
#        [ 0.53853487,  0.79409684, -1.07606899]])
r + s
# array([[-1.2665719 , -0.89170906, -2.62863251],
#        [ 1.45438796, -1.40971615,  2.33016495],
#        [-0.42266431, -1.19224027,  0.73016336],
#        [ 0.37383524,  3.10917626, -0.89696358]])

# NuinPy还支持所谓的广播
2 * r + 3
# array([[ 1.2771561 ,  0.78846104, -0.45791214],
#        [ 1.94611314,  4.77117199,  7.18234622],
#        [ 5.35157558,  1.36528496,  1.41957468],
#        [ 2.67060074,  7.63015885,  3.35821083]])
s=np.random.standard_normal(3)
# array([-0.97683636,  0.26286729, -1.17060774])
r
# array([[-0.86142195, -1.10576948, -1.72895607],
#        [-0.52694343,  0.885586  ,  2.09117311],
#        [ 1.17578779, -0.81735752, -0.79021266],
#        [-0.16469963,  2.31507942,  0.17910542]])
r + s
# array([[-0.86142195, -1.10576948, -1.72895607],
#        [-0.52694343,  0.885586  ,  2.09117311],
#        [ 1.17578779, -0.81735752, -0.79021266],
#        [-0.16469963,  2.31507942,  0.17910542]])
# r和s的第一行相加
r.transpose()  # 转置
# array([[-0.86142195, -0.52694343,  1.17578779, -0.16469963],
#        [-1.10576948,  0.885586  , -0.81735752,  2.31507942],
#        [-1.72895607,  2.09117311, -0.79021266,  0.17910542]])
np.shape(r.T)  # (3, 4)


def f(x):
    return 3 * x + 5

f(0.5)  # 6.5
f(r)
# array([[  2.41573416,   1.68269156,  -0.1868682 ],
#        [  3.41916971,   7.65675799,  11.27351933],
#        [  8.52736337,   2.54792744,   2.62936202],
#        [  4.50590111,  11.94523827,   5.53731625]])

4.5 内存布局

x = np.random.standard_normal((5, 10000000))
y = 2 * x + 3
C = np.array((x, y), order='C') # 类 C 语言内存布局(行优先)
F = np.array((x, y), order='F') # 类 Fortran 内存布局(列优先)
x = 0.0
y = 0.0  # memory cleanup

%timeit C.sum()
# 1 loop, best of 3: 146 ms per loop

%timeit F.sum()
# 1 loop, best of 3: 145 ms per loop

%timeit C[0].sum(axis=0)  # 对每列元素求和
# 10 loops, best of 3: 130 ms per loop
%timeit C[0].sum(axis=1)  # 对每行元素求和
# 10 loops, best of 3: 73.5 ms per loop

行元素求和速度更快,这是因为每行元素都在相邻的存储位置上
使用类Fortran内存布局:

%timeit F[0].sum(axis=0)  # 对每列元素求和
# 1 loop, best of 3: 282 ms per loop
%timeit F[0].sum(axis=1)  # 对每行元素求和
# 1 loop, best of 3: 241 ms per loop

按道理来说使用类Fortran内存布局,对每列元素求和比对每行元素求和的时间应该短,因为类 Fortran 内存布局是列优先
但是实际测试的时候还是对每行元素求和时间短-_-||
与类 C 语言变种相比,整体操作绝对要慢得多。

你可能感兴趣的:(Python金融大数据分析)