Matplotlib.pyplot未使用set_数据更新动态线图

2024-04-19 10:41:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图创建一个随着x和y数据集长度的变化而动态变化的绘图

我通过绘制每个循环的曲线图来实现这一点,但这并不是实现这一点的有效方法

我使用第165行中的变量self.line初始化此绘图。在第228行,我更改了绘图self.ax2.plot(self.stepData, self.nAlive, 'b-')

我试图用它来改变数据:``self.line.set_data=(self.stepData,self.nAlive)

这是我的密码:


import random
import numpy as np
import matplotlib.pyplot as plt


# ====================================================================================

class Person:

    def __init__(self, position):
        self.xpos = position[0]
        self.ypos = position[1]
        self.alive = True
        self.trapped = False  # if all people are trapped, need to end program
        self.distance = None  # distance to closest neighbor

    def move(self, xmax, ymax, bc, taken):
        """ moves person, if possible, to an adjacent corner """
        # cannot step where someone else is currently standing
        taken[self.xpos, self.ypos] = False  # moving from current spot
        disallowed = set()  # empty set object; will add disallowed directions
        if bc == 'wall':
            if self.ypos == ymax or taken[self.xpos, self.ypos + 1]:
                disallowed.add('north')
            if self.xpos == xmax or taken[self.xpos + 1, self.ypos]:
                disallowed.add('east')
            if self.ypos == 0 or taken[self.xpos, self.ypos - 1]:
                disallowed.add('south')
            if self.xpos == 0 or taken[self.xpos - 1, self.ypos]:
                disallowed.add('west')
        elif bc == 'periodic':
            if (self.ypos == ymax and taken[self.xpos, (self.ypos + 1) % ymax]) \
                    or (self.ypos < ymax and taken[self.xpos, self.ypos + 1]):
                disallowed.add('north')
            if (self.xpos == xmax and taken[(self.xpos + 1) % xmax, self.ypos]) \
                    or (self.xpos < xmax and taken[self.xpos + 1, self.ypos]):
                disallowed.add('east')
            if (self.ypos == 0 and taken[self.xpos, (self.ypos - 1) % ymax]) \
                    or (self.ypos > 0 and taken[self.xpos, self.ypos - 1]):
                disallowed.add('south')
            if (self.xpos == 0 and taken[(self.xpos - 1) % xmax, self.ypos]) \
                    or (self.xpos > 0 and taken[self.xpos - 1, self.ypos]):
                disallowed.add('west')

        # Use the set method 'difference' to get set of allowed directions
        allowed = {'north', 'east', 'south', 'west'}.difference(disallowed)

        if len(allowed) == 0:
            self.trapped = True  # cannot move anymore
        else:
            """
            Randomly pick from the allowed directions; need to convert set
            object to a list because random.choice doesn't work on sets
            """
            self.direction = random.choice(list(allowed))
            if self.direction == 'north':
                if (bc == 'wall' and self.ypos < ymax) or bc == 'periodic':
                    self.ypos += 1
            elif self.direction == 'east':
                if (bc == 'wall' and self.xpos < xmax) or bc == 'periodic':
                    self.xpos += 1
            elif self.direction == 'south':
                if (bc == 'wall' and self.ypos > 0) or bc == 'periodic':
                    self.ypos -= 1
            elif self.direction == 'west':
                if (bc == 'wall' and self.xpos > 0) or bc == 'periodic':
                    self.xpos -= 1
            """

            With periodic boundary conditions, it's possible that (xpos, ypos) could
            be off the grid (e.g., xpos < 0 or xpos > xmax). The Python modulo
            operator can be used to give exactly what we need for periodic bc. For
            example, suppose xmax = 20; then if xpos = 21, 21 % 20 = 1; if xpos = -1,
            -1 % 20 = 19. (Modulo result on a negative first argument may seem
            strange, but it's intended for exactly this type of application. Cool!)
            If 0 <= xpos < xmax, then modulo simply returns xpos. For example,
            0 % 20 = 0, 14 % 20 = 14, etc. Only special case is when xpos = xmax, in
            which case we want to keep xpos = xmax and not xpos % xmax = 0
            """
            if self.xpos != xmax:
                self.xpos = self.xpos % xmax
            if self.ypos != ymax:
                self.ypos = self.ypos % ymax

    def proximity(self, xmax, ymax, bc, people):
        """
        Finds distance from closest neighbor, will calculate actual distance over diagonals, assuming 1 square is
        1 ft^2. If no one else is in range (less than 6 feet away), return None.
        """
        distance = None

        for person in people:
            if person is not self and person.alive:  # can only get infected by other alive people
                x = abs(self.xpos - person.xpos)
                y = abs(self.ypos - person.ypos)

                if bc == 'periodic':  # check other direction (across boundaries) for periodic bc
                    tempX1 = None
                    tempY1 = None
                    for i in range(2):
                        if tempX1 is None:
                            tempX1 = (self.xpos - 0) + (xmax - person.xpos)  # check distance in one direction
                        else:
                            tempX2 = (person.xpos - 0) + (xmax - self.xpos)  # check distance in other direction
                            if tempX2 < tempX1:
                                tempX1 = tempX2
                        if tempY1 is None:
                            tempY1 = (self.ypos - 0) + (ymax - person.ypos)  # check distance in one direction
                        else:
                            tempY2 = (person.ypos - 0) + (ymax - self.ypos)  # check distance in other direction
                            if tempY2 < tempY1:
                                tempY1 = tempY2
                    if tempX1 < x:
                        x = tempX1
                    if tempY1 < y:
                        y = tempY1

                if x >= 6 or y >= 6:
                    pass  # not close enough to infect
                else:  # need to find distance
                    temp = np.sqrt(x ^ 2 + y ^ 2)
                    if distance is None or temp < distance:
                        distance = temp
        if distance is not None and distance >= 6:
            distance = None
        return distance

    def gambleYourLife(self, distance):
        if distance is not None:  # chance of dying!!
            p = 0.5 * np.e ** (-0.3 * distance)
            if np.random.binomial(1, p) == 1:
                self.alive = False


# ====================================================================================


class Earth:

    def __init__(self, people, gridSize, bc, nSteps):

        # if nSteps = None, goes on until 1 person left
        """
        Grid class takes people inputs, along with size, boundary conditions, and number of maximum steps, and runs
        on a random walk until one person is left or until nSteps is reached, depending on user input.
        """
        self.people = people
        self.xmax = gridSize[0]
        self.ymax = gridSize[1]
        self.bc = bc
        self.point = []
        self.nSteps = nSteps
        self.nAlive = [len(self.people)]  # keep track of number of people alive after each step
        self.stepData = [0]  # list of each step
        # array to keep track of points that have are taken
        self.taken = np.zeros([self.xmax + 1, self.ymax + 1], dtype=bool)

        fig, (self.ax1, self.ax2) = plt.subplots(1, 2)  # create new figure window and ax (plot) attributes to Earth obj
        self.ax1.set_xlim(0, self.xmax)
        self.ax1.set_ylim(0, self.ymax)

        self.ax2.set_xlim(0, nSteps)
        self.ax2.set_ylim(0, len(people) + 10)
        self.line, = self.ax2.plot(self.stepData, self.nAlive, 'b-')
        self.ax2.plot(self.stepData, self.nAlive, '--')
        self.ax2.set_xlabel('Number of steps')
        self.ax2.set_ylabel('Number of people alive')

        self.ax2.set_autoscalex_on(True)
        # self.ax2.autoscale_view(True, True, False)

        for p in self.people:
            pnt, = self.ax1.plot([p.xpos], [p.ypos], 'bo')
            self.taken[p.xpos, p.ypos] = True
            self.point.append(pnt)

    def go(self):
        step = 1
        while not all([p.trapped for p in self.people]) and (self.nSteps is None or step < self.nSteps):
            currAlive = 0  # number of alive each round
            for i, p in enumerate(self.people):
                if not p.trapped and p.alive:
                    p.move(self.xmax, self.ymax, self.bc, self.taken)
                    # update grid
                    self.point[i].set_data(p.xpos, p.ypos)
                    self.point[i].set_marker('o')
                    self.taken[p.xpos, p.ypos] = True
                    """
                    When using periodic boundary conditions, a position on a
                    wall is identical to the corresponding position on the
                    opposite wall. So if a walker visits (x, ymax) then
                    (x, 0) must also be marked as visited; if a walker vists
                    (0, y) then (xmax, y) must also be marked as visited; etc.
                    """
                    if self.bc == 'periodic':
                        if p.xpos == self.xmax:
                            self.taken[0, p.ypos] = True
                        elif p.xpos == 0:
                            self.taken[self.xmax, p.ypos] = True
                        if p.ypos == self.ymax:
                            self.taken[p.xpos, 0] = True
                        elif p.ypos == 0:
                            self.taken[p.xpos, self.ymax] = True

                    # plot path lines
                    if p.direction == 'north':
                        self.ax1.vlines(p.xpos, p.ypos - 1, p.ypos)
                    elif p.direction == 'east':
                        self.ax1.hlines(p.ypos, p.xpos - 1, p.xpos)
                    elif p.direction == 'south':
                        self.ax1.vlines(p.xpos, p.ypos + 1, p.ypos)
                    elif p.direction == 'west':
                        self.ax1.hlines(p.ypos, p.xpos + 1, p.xpos)

                    # determine proximity to others and potentially die
                    neighbor = p.proximity(self.xmax, self.ymax, self.bc, self.people)
                    p.gambleYourLife(neighbor)
                    if not p.alive:
                        self.point[i].set_color('r')
                    else:  # still alive
                        currAlive += 1

            # update plot
            self.nAlive.append(currAlive)
            self.stepData.append(step)
            # self.line.set_data = (self.stepData, self.nAlive)
            self.ax2.plot(self.stepData, self.nAlive, 'b-')


            step += 1
            plt.pause(0.2)


# ====================================================================================


def program(nPeople=10, gridSize=(20, 20), bc='wall', nSteps=100):
    initPositions = set()  # set of tuple of initial position of each person
    flock = []  # initialize - list of all people
    for p in range(nPeople):
        while True:
            x = random.randint(0, gridSize[0])  # randomly place person on graph, only one person per point
            y = random.randint(0, gridSize[1])
            if (x, y) not in initPositions:
                break
        initPositions.add((x, y))
        tempPerson = Person(position=(x, y))
        flock.append(tempPerson)  # add person to list of people
    flock = tuple(flock)  # turn list into tuple (not sure why... not sure if necessary)

    pandemic = Earth(flock, gridSize, bc, nSteps)
    pandemic.go()


# main program =======================================================================

program()

Tags: orandofselfifpeopletakenperson
1条回答
网友
1楼 · 发布于 2024-04-19 10:41:20

您必须使用命令plt.ion()打开交互模式,或者使用fig.show(block=False)使用参数block=False显示图形“未被阻止”

下面是使用plt.ion()的示例:

# %% 
# first cell                

import matplotlib.pyplot as plt

x = [1,2,3]
y = [1,7,5]

plt.ion()               # interactive mode on
fig,ax = plt.subplots()
line, = ax.plot(x,y)
fig.show()

# %% 
# second cell                

x.append(4)
y.append(10)

line.set_data(x,y)   # set new data

ax.relim()           # recompute the data limits
ax.autoscale_view()  # automatic axis scaling

# %% 
# third cell: just a radom example to plot sequentially           -

from random import randint

for i in range(5):
    x.append(x[-1]+1)
    y.append(randint(0,10))

    line.set_data(x,y)   # set new data

    ax.relim()           # recompute the data limits
    ax.autoscale_view()  # automatic axis scaling

    plt.pause(0.5)

相关问题 更多 >