其他
示例:随机漫步
我们通过模拟随机漫步来说明如何运用数组运算。先来看一个简单的随机漫步的例子:从 0 开始,步长 1 和 -1 出现的概率相等。
下面是一个通过内置的random
模块以纯 Python 的方式实现 1000 步的随机漫步:
In [247]: import random
.....: position = 0
.....: walk = [position]
.....: steps = 1000
.....: for i in range(steps):
.....: step = 1 if random.randint(0, 1) else -1
.....: position += step
.....: walk.append(position)
.....:
2
3
4
5
6
7
8
9
图 4-4 是根据前 100 个随机漫步值生成的折线图:
In [249]: plt.plot(walk[:100])

不难看出,这其实就是随机漫步中各步的累计和,可以用一个数组运算来实现。因此,我用np.random
模块一次性随机产生 1000 个“掷硬币”结果(即两个数中任选一个),将其分别设置为 1 或 -1,然后计算累计和:
In [251]: nsteps = 1000
In [252]: draws = np.random.randint(0, 2, size=nsteps)
In [253]: steps = np.where(draws > 0, 1, -1)
In [254]: walk = steps.cumsum()
2
3
4
5
6
7
有了这些数据之后,我们就可以沿着漫步路径做一些统计工作了,比如求取最大值和最小值:
In [255]: walk.min()
Out[255]: -3
In [256]: walk.max()
Out[256]: 31
2
3
4
5
现在来看一个复杂点的统计任务——首次穿越时间,即随机漫步过程中第一次到达某个特定值的时间。假设我们想要知道本次随机漫步需要多久才能距离初始 0 点至少 10 步远(任一方向均可)。np.abs(walk)>=10
可以得到一个布尔型数组,它表示的是距离是否达到或超过 10,而我们想要知道的是第一个 10 或 -10 的索引。可以用argmax
来解决这个问题,它返回的是该布尔型数组第一个最大值的索引(True
就是最大值):
In [257]: (np.abs(walk) >= 10).argmax()
Out[257]: 37
2
注意,这里使用argmax
并不是很高效,因为它无论如何都会对数组进行完全扫描。在本例中,只要发现了一个True
,那我们就知道它是个最大值了。
一次模拟多个随机漫步
如果你希望模拟多个随机漫步过程(比如 5000 个),只需对上面的代码做一点点修改即可生成所有的随机漫步过程。只要给numpy.random
的函数传入一个二元元组就可以产生一个二维数组,然后我们就可以一次性计算 5000 个随机漫步过程(一行一个)的累计和了:
In [258]: nwalks = 5000
In [259]: nsteps = 1000
In [260]: draws = np.random.randint(0, 2, size=(nwalks, nsteps)) # 0 or 1
In [261]: steps = np.where(draws > 0, 1, -1)
In [262]: walks = steps.cumsum(1)
In [263]: walks
Out[263]:
array([[ 1, 0, 1, ..., 8, 7, 8],
[ 1, 0, -1, ..., 34, 33, 32],
[ 1, 0, -1, ..., 4, 5, 4],
...,
[ 1, 2, 1, ..., 24, 25, 26],
[ 1, 2, 3, ..., 14, 13, 14],
[ -1, -2, -3, ..., -24, -23, -22]])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
现在,我们来计算所有随机漫步过程的最大值和最小值:
In [264]: walks.max()
Out[264]: 138
In [265]: walks.min()
Out[265]: -133
2
3
4
5
得到这些数据之后,我们来计算 30 或 -30 的最小穿越时间。这里稍微复杂些,因为不是 5000 个过程都到达了 30。我们可以用any
方法来对此进行检查:
In [266]: hits30 = (np.abs(walks) >= 30).any(1)
In [267]: hits30
Out[267]: array([False, True, False, ..., False, True, False], dtype=bool)
In [268]: hits30.sum() # Number that hit 30 or -30
Out[268]: 3410
2
3
4
5
6
7
然后我们利用这个布尔型数组选出那些穿越了 30(绝对值)的随机漫步(行),并调用argmax
在轴 1 上获取穿越时间:
In [269]: crossing_times = (np.abs(walks[hits30]) >= 30).argmax(1)
In [270]: crossing_times.mean()
Out[270]: 498.88973607038122
2
3
4
请尝试用其他分布方式得到漫步数据。只需使用不同的随机数生成函数即可,如normal
用于生成指定均值和标准差的正态分布数据:
In [271]: steps = np.random.normal(loc=0, scale=0.25,
.....: size=(nwalks, nsteps))
2
补充
genfromtxt
Numpy 中导入数据方式有多种,其中函数之一为 genfromtxt ,genformtxt 相对其他函数有自己的优势和劣势,
- 劣势:相对其它函数数据加载慢,从数据读入到返回需要两个迭代循环,第一个迭代将文件中每一行转化为一个字符串序列,第二个循环迭代对每个字符串序列指定合适的数据类型;源于这两个迭代原因,相对其它函数会慢一点;
- 优势:更灵活,数据加载时会能够处理丢失数据,例如对某些 missing_values 做一些数据填充,或将某一列数据从一中形式转化为另一种形式;
函数语法如下:
**genfromtxt(fname,dtype,comments,delimiter,skipd_header,skip_fonter,converters,missing_values,filling_values,usecols,names, autostrip,kwarg)
- fname -> str/list /IO Object 等等,表示数据源;文件路径,字符串列表、StringIO 对象都可;
- dtype -> dtype,最终数组的数据类型;
- comments -> str,注释标识符,加载时会自动忽略位于注释标识符后面的字符串;
- delimiter -> str/int/sequence,分割符,加载时来分割字符串,比较常见的就是逗号,也可以指定 int 整形表示每一个元素最大宽度;也可以是字符列表,表示分割符可为多个字符;
- skip_header -> int,数据加载时跳过文件头部的字符行数;
- skip_footer -> int,数据加载时跳过文件尾部字符串行数;
- converters -> variable,可以是字典形式也可以是 lambda 函数,表示将某一种数据格式转化为另一种数据格式;
- missing_values -> variable,指定数组中 missing value;
- filling_values -> variable,指定 filling_values,来替代 missing_value;
- usercols -> sequence,指定需读取的列数, 0 表示第一列,-1 为最后一列;
上面列举出各参数基本用法后,下面将针对里面每个参数,结合实例详细介绍其用法
fname 参数
入的 fname 是以 gz 结尾的话,读取时会在加载数据之前自动解压,但较为常见的都是 txt 文本
delimiter参数
当 delimiter 为字符时,表示分割符,元素以分割符进行分割
>>> data = u"1, 2, 3\n4, 5, 6"
>>> np.genfromtxt(StringIO(data), delimiter=",")
array([[ 1., 2., 3.],
[ 4., 5., 6.]])
2
3
4
当 delimiter 表示整数元组时,代表每一列所在元素最大宽度;如下定义了每一列元素最大宽度分别为4,3,2;
>>> data = u" 1 2 3\n 4 5 67\n890123 4"
>>> np.genfromtxt(StringIO(data), delimiter=3)
array([[ 1., 2., 3.],
[ 4., 5., 67.],
[ 890., 123., 4.]])
>>> data = u"123456789\n 4 7 9\n 4567 9"
>>> np.genfromtxt(StringIO(data), delimiter=(4, 3, 2))
array([[ 1234., 567., 89.],
[ 4., 7., 9.],
[ 4., 567., 9.]])
2
3
4
5
6
7
8
9
10
autostrip 参数
对于每行字符串用 分割符分割后,得到的单个元素可能会含有一些制表符或空格等标识符;autostrip 参数设为 True,元素中的空格标识符会被自动删除~
>>> data = u"1, abc , 2\n 3, xxx, 4"
>>> # Without autostrip
>>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|U5")
array([['1', ' abc ', ' 2'],
['3', ' xxx', ' 4']], dtype='<U5')
>>> # With autostrip
>>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|U5", autostrip=True)
array([['1', 'abc', '2'],
['3', 'xxx', '4']], dtype='<U5')
2
3
4
5
6
7
8
9
comments 参数
comments表示注释标识符,指定后注释标识符所在行后面的字符会被自动省略,如下:
>>> data = u"""#
... # Skip me !
... # Skip me too !
... 1, 2
... 3, 4
... 5, 6 #This is the third line of the data
... 7, 8
... # And here comes the last line
... 9, 0
... """
>>> np.genfromtxt(StringIO(data), comments="#", delimiter=",")
array([[1., 2.],
[3., 4.],
[5., 6.],
[7., 8.],
[9., 0.]])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
skip_header,skip_footer
读取数据时,可能前几行后者后几行的数据我们不需要,此时设定 skip_header、skip_footer 参数即可,例如读取时 想过滤数据列表中 前三行、后五行数据,此时参数设定时 skip_header=3,skip_footer=5
即可
>>> data = u"\n".join(str(i) for i in range(10))
>>> np.genfromtxt(StringIO(data),)
array([ 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
>>> np.genfromtxt(StringIO(data),
... skip_header=3, skip_footer=5)
array([ 3., 4.])
2
3
4
5
6
usecols
usecols 参数来指定只读取数据中指定的列,与数组索引用法相似,下面是只提取原数据中第一列和最后一列数据:
>>> data = u"1 2 3\n4 5 6"
>>> np.genfromtxt(StringIO(data), usecols=(0, -1))
array([[ 1., 3.],
[ 4., 6.]])
2
3
4
converters
converters 用来对数组中一些数据进行格式替换,以字典形式定义 key 表示需要数据转化的列索引,value 为转换函数,下面实例中将百分比转化为浮点型
>>> data = u"1, , 3\n 4, 5, 6"
>>> convert = lambda x: float(x.strip() or -999)
>>> np.genfromtxt(StringIO(data), delimiter=",",
... converters={1: convert})
array([[ 1., -999., 3.],
[ 4., 5., 6.]])
2
3
4
5
6
missing_values,filling_values
读取的数据中一般出现缺失值,而这些缺失值有时对最终分析结果影响很大,我们需要做的就是将这些缺失值用某些值来代替,这里就需要用到 missing_values 、filling_values ,前者标记缺失值,后者对missing_values 进行填充 ,
例如下面的实例,分别将 0,1,2 列数据中的 'N/A'、空格、‘???’ 替换为 0,0,-999;
>>> data = u"N/A, 2, 3\n4, ,???"
>>> kwargs = dict(delimiter=",",
... dtype=int,
... names="a,b,c",
... missing_values={0:"N/A", 'b':" ", 2:"???"},
... filling_values={0:0, 'b':0, 2:-999})
>>> np.genfromtxt(StringIO(data), **kwargs)
array([(0, 2, 3), (4, 0, -999)],
dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])
2
3
4
5
6
7
8
9