首页 > 解决方案 > 将结构化数据附加到 Python 中的类属性

问题描述

我有几个用于运行数值模拟的对象。下面显示了一个最小示例,其中有两个对象: 1) 一个Environment对象具有两种状态(x 和 y),它通过时间随机模拟;2)Simulation管理模拟并在整个模拟过程中保存环境状态的对象。

在 Simulation 对象中,我想保存 Environment 的状态 1) 通过时间和 2) 在多个模拟中。随着时间的推移,我可以使用 defaultdict 在单个模拟中保存状态变量,但在模拟中,我不清楚保存已生成的 defaultdict 的最佳方法。如果我附加到一个列表(不使用副本),那么由于列表的可变性,该列表将返回所有相同的默认字典。在下面的示例中,我使用,正如这里copy.copy的答案所暗示的那样。

是否有更“Pythonic”的方法?使用不可变类型来存储每个模拟的默认字典会更好吗?

import copy
from collections import defaultdict
import numpy as np, pandas as pd
from matplotlib import pyplot as plt


class Environment(object):
    """
    Class representing a random walk of two variables x and y

    Methods
    -------
    start_simulation:   draw values from state variables from priors
    step:               add random noise to state variables
    current_state:      return current state of x and y in a dict

    """
    def __init__(self, mu1, sigma1, mu2, sigma2):
        self.mu1 = mu1
        self.mu2 = mu2
        self.sigma1 = sigma1
        self.sigma2 = sigma2

    def start_simulation(self):
        self.x = self.mu1 + self.sigma1 * np.random.randn()
        self.y = self.mu2 + self.sigma2 * np.random.randn()

    def step(self):
        self.x += self.sigma1 * np.random.randn()
        self.y += self.sigma2 * np.random.randn()

    def current_state(self):
        return({"x": self.x, "y": self.y})


class Simulation(object):
    """
    Class representing a simulation object for handling the Environment object
     and storing data

    Methods
    -------

    start_simulation:   start the simulation; initialise state of the environment
    simulate:           generate n_simulations simulations of n_timesteps time steps each
    save_state:          
    """
    def __init__(self, env, n_timesteps):
        self.env = env
        self.n_timesteps = n_timesteps

        self.data_all = []
        self.data_states = defaultdict(list)

    def start_simulation(self):
        self.timestep = 0
        self.env.start_simulation()

        # Append current data (if non empty)
        if self.data_states:
            self.data_all.append(copy.copy(self.data_states)) # <---------- this step
            # without copy.copy this will return all elements of the list data_all to be the 
            # same default dict at the end of all simulations - lists are mutable

        # Reset data_current
        self.data_states = defaultdict(list)

    def simulate(self, n_simulations):
        """
        Run simulation for n_simulations and n_timesteps timesteps
        """
        self.start_simulation()

        for self.simulation in range(n_simulations):

            self.timestep = 0

            while(self.timestep < self.n_timesteps):
                self.env.step()
                self.save_state(self.env.current_state())
                self.timestep += 1

            self.start_simulation()


    def save_state(self, state):
        """
        Save results to a default dict
        """
        for key, value in state.items():
            self.data_states[key].append(value)


if __name__ == "__main__":

    # Run 7 simulations, each for for 20 time steps
    N_TIME = 20
    N_SIM = 7

    e = Environment(
        mu1 = 1.4, sigma1 = 0.1, 
        mu2 = 2.6, sigma2 = 0.05)

    s = Simulation(env = e, n_timesteps = N_TIME)
    s.simulate(N_SIM)

    # Plot output
    fig, ax = plt.subplots()
    for var, c in zip(["x", "y"], ["#D55E00", "#009E73"]):
        [ax.plot(pd.DataFrame(d)[var], label = var, color = c) for d in s.data_all]
    ax.set_xlabel("Time")
    ax.set_ylabel("Value")
    plt.show()

随机游走python

标签: pythonclassdesign-patterns

解决方案


推荐阅读