Python编程学习笔记 - 随机漫步

0. 随机漫步

我们将使用Python来生成随机漫步数据,再使用matplotlib以引人瞩目的方式将这些数据呈现出来。随机漫步是这样行走得到的路径:每次行走都完全是随机的,没有明确的方向,结果是由一系列随机决策决定的。

在自然界、物理界、生物学、化学和经济领域,随机漫步都有其实际用途。例如:漂浮在水滴上的花粉因不断受到水分子的挤压而在水面上移动。水滴中的分子运动是随机的,因此花粉在水面上的运动路径犹如随机漫步。

1. 创建RandomWalk()类

为模拟随机漫步,我们将创建一个名为RandomWalk的类,它随机地选择前进方向。这个类需要三个属性,其中一个是存储随机漫步次数的变量,其他两个是列表,分别存储随机漫步经过的每个点的x和y的坐标。

RandomWalk类只包含两个方法:init()和fill_walk(),其中后者计算随机漫步经过的所有点。先来看看__init__():

from random import choice
    
class RandomWalk():
    """ a class to genetate random walk data """

    def __init__(self, num_points=5000):
        self.num_points = num_points

        self.x_values = [0]
        self.y_values = [0]

为做出随机决策,所有可能的选择都存储在一个列表中,并在每次做决策时都使用choice()来决定使用哪种选择。接下来,我们将随机漫步包含的默认点数设置为5000点,这大到足以生成有趣的模式,同时有足够小,可确保能够快速地模拟随机漫步。然后,我们创建了两个用于存储x和y值得列表,并让每次漫步都从点(0, 0)出发。

2. 选择方向

我们将使用fill_walk()来生成漫步包含的点, 并决定每次漫步的方向。

    def fill_walk(self):
        while len(self.x_values) < self.num_points:
            x_direction = choice([1, -1])
            x_distance  = choice([0, 1, 2, 3, 4])
            x_step = x_direction * x_distance

            y_direction = choice([1, -1])
            y_distance  = choice([0, 1, 2, 3, 4])
            y_step = y_direction * y_distance

            if x_step == 0 and y_step == 0:
                continue

            next_x = self.x_values[-1] + x_step
            next_y = self.y_values[-1] + y_step

            self.x_values.append(next_x)
            self.y_values.append(next_y)

我们建立了一个循环,这个循环不断运行,直到漫步包含所数量的点。这个方法的主要部分告诉Python如何模拟四种漫步决定:向右走还是向左走?焱指定的方向走多远?向上走还是向下走?沿选定的方向走多远?

我们使用choice([1, -1])给x_direction选择一个值,结果要么是表示向右走的1,要么是表示向左走的-1。接下来,choice([0,1,2,3,4])随机地选择一个0~4之间的整数,告诉Python沿指定的方向走多远(x_distance)。(通过包含0,我们不仅能够沿两个轴移动,还能够沿y轴移动。)

我们将移动方向乘以移动距离,以确定沿x和y轴移动的距离。如果 x_step为正,将向右移动,为负将向左移动,而为零将垂直移动;如果y_step为正,就意味着向上移动,为负意味着向下移动,而为零意味着水平移动。如果x_step和y_step都为零,则意味着原地踏步,我们拒绝这样的情况,接着执行下一次循环。

为获取漫步中下一个点的x值,我们将x_step与x_values中的最后一个值相加,对于y值也做相同的处理。获得下一个点的x值和y值后,我们将他们分别附加到列表x_values和y_values的末尾。

3. 绘制随机漫步图

下面的代码将随机漫步的所有点都绘制出来:

import matplotlib.pyplot as plt

from random_walk import RandomWalk

rw = RandomWalk()
rw.fill_walk()

plt.scatter(rw.x_values, rw.y_values, s=15)

plt.show()

我们首先导入了模块pyplot和RandomWalk类,然后创建了一个RandomWalk实例,并将其存储到rw中,再调用fill_walk()。我们将随机漫步包含的x和y值传递给scatter(),并选择了合适的点尺寸。下图包含了包含了5000个点的随机漫步图:
在这里插入图片描述

4. 模拟多次随机漫步

每次随机漫步都不同,因此探索可能生成的各种模式很有趣。要在不多次运行程序的情况下使用前面的代码模拟多次随机漫步,一种办法是将这些代码放在一个while循环中:

import matplotlib.pyplot as plt

from random_walk import RandomWalk

while True:
    rw = RandomWalk()
    rw.fill_walk()
    plt.scatter(rw.x_values, rw.y_values, s=15)
    plt.show()

    keep_running = input("Make another walk? (y/n):")
    if keep_running == 'n':
        break

关闭查看器,程序将询问你, 如果你输入y,可继续模拟一次随机漫步,要结束模拟,请输入n。

5. 设置随机漫步图的样式

我们将定制图表,以突出每次漫步的重要特征,并让分散注意力的元素不那么显眼。为此,我们确定要突出的元素,如漫步的起点、终点和经过的路径。接下来确定要使其不那么显眼的元素,如刻度标记和标签。最终的结果是简单的可视化表示,清楚地指出了每次漫步经过的路径。

5.1 给点着色

我们使用颜色映射来指出漫步中各点的先后顺序,并删除每个点的黑色轮廓,让它们的颜色更明显。为根据漫步中各点的先后顺序进行着色,我们传递参数c,并将其设置为一个列表,其中包含各点的先后顺序。由于这些点是按照顺序绘制的,因此给参数c指定的列表只需包含数字1~5000,如下所示:

import matplotlib.pyplot as plt
from random_walk import RandomWalk

while True:
	rw = RandomWalk()
	rw.fill_walk()

	point_numbers = list(range(rw.num_points))
	plt.scatter(rw.x_values, rw.y_values, c=point_numbers, 
			 cmap=plt.cm.Blues, edgecolors='None', s=15)

	plt.show()

	keep_running = input("Make another walk? (y/n):")
	if keep_running == 'n':
		break

我们使用了range()生成了一个数字序列,其中包含的数字个数与漫步包含的点数相同。接下来,我们将这个列表存储在point_numbers中,以便后面使用它来设置每个漫步点的颜色。我们将参数c设置为point_numbers,指定使用颜色映射Blues,并传递实参edgecolor=None 以删除每个点周围的轮廓。最终的随机漫步图从浅蓝色渐变为深蓝色,如下图:
在这里插入图片描述

5.2 重新绘制起点和终点

我们让起点和终点变得更大,并显示为不同的颜色,以突出它们,如下所示:

	plt.scatter(0, 0, c='green',edgecolor='None', s=100)
	plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolor='None', s=100)

为突出起点,我们使用绿色绘制点(0, 0),并使其比其他点大(s=100)。为突出终点,我们在漫步包含的最后一个x和y值处绘制一个点,将其颜色设置为红色,并将尺寸设置为100。请务必将这些代码放在调用plt.show()的代码前面,确保在其他点的上面绘制起点和终点。
在这里插入图片描述

5.3 隐藏坐标轴

下面来隐藏图标中的坐标轴,以免我们注意的是坐标轴而不是随机漫步路径。要隐藏坐标轴,可使用如下代码:

	plt.axes().get_xaxis().set_visible(False)
	plt.axes().get_yaxis().set_visible(False)
5.4 增加点数

增加点数,以提供更多的数据。为此,我们在创建RandomWalk实例时增大num_points的值,并在绘制时调整每个点的大小,如下所示:

	rw = RandomWalk(50000)

完整代码如下所示:

import matplotlib.pyplot as plt

from random_walk import RandomWalk

while True:
    rw = RandomWalk(50000)
    rw.fill_walk()

    point_numbers = list(range(rw.num_points))

    plt.scatter(rw.x_values, rw.y_values, c=point_numbers, 
             cmap=plt.cm.Blues, edgecolors='None', s=1)

    plt.scatter(0, 0, c='green',edgecolor='None', s=100)
    plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolor='None', s=100)
    
    plt.axes().get_xaxis().set_visible(False)
    plt.axes().get_yaxis().set_visible(False)

    plt.show()

    keep_running = input("Make another walk? (y/n):")
    if keep_running == 'n':
        break

下图模拟了一次50000个点的随机漫步,并将每个点的大小调整为1。最终的随机漫步图更纤细,犹如云朵,像一件艺术品。
在这里插入图片描述

5.5 调整尺寸以适应屏幕

图表适合屏幕大小时,更能有效地将数据中的规律呈现出来。为让绘图窗口更适应屏幕大小,可像下面这样调整matplotlib输出的尺寸:

...
	plt.figure(figsize=(10, 6))
	or 
	plt.figure(dpi=128, figsize=(10, 6))
...

6. 改进的随机漫步

方法fill_walk()很长。新建了一个get_step()的方法,用于确定每次漫步的距离和方向,并计算这次漫步将如何移动。然后,在fill_walk()中调用get_step()两次 。这样可缩小fill_walk()的规模,让这个方法阅读和理解起来更容易。

另外,列表xy_direction和xy_distance作了些许更新。

random_walk.py 原代码如下:

from random import choice
    
class RandomWalk():
    """ a class to genetate random walk data """

    def __init__(self, num_points=5000):
        self.num_points = num_points

        self.x_values = [0]
        self.y_values = [0]

    def get_step(self):
            xy_direction = choice([1, 0, -1])
            xy_distance  = choice([0, 1, 2, 3, 4, 5, 6, 7, 8])
            return xy_direction * xy_distance
    
    def fill_walk(self):
        while len(self.x_values) < self.num_points:
            x_step = self.get_step()
            y_step = self.get_step()

            if x_step == 0 and y_step == 0:
                continue

            next_x = self.x_values[-1] + x_step
            next_y = self.y_values[-1] + y_step

            self.x_values.append(next_x)
            self.y_values.append(next_y)

rw_visual.py 原代码如下

import matplotlib.pyplot as plt
    
from random_walk import RandomWalk

while True:
    rw = RandomWalk(50000)
    rw.fill_walk()

    plt.figure(dpi=128, figsize=(10, 6))

    point_numbers = list(range(rw.num_points))

    plt.scatter(rw.x_values, rw.y_values, c=point_numbers, 
             cmap=plt.cm.Blues, edgecolors='None', s=1)

    plt.scatter(0, 0, c='green',edgecolor='None', s=20)
    plt.scatter(rw.x_values[-1], rw.y_values[-1], c='red', edgecolor='None', s=20)
    
    plt.axes().get_xaxis().set_visible(False)
    plt.axes().get_yaxis().set_visible(False)

    plt.show()
    #plt.savefig('rw_visual.png', bbox_inches='tight')

    #keep_running = input("Make another walk? (y/n):")
    #if keep_running == 'n':
    #    break
    break

生成的图跟以前差不多:
在这里插入图片描述

文章内容来自《Python编程 从入门到实践》 [美] Eric Matthes 袁国忠 译

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值