How to Collect Data(如何收集数据)

  数据收集主要由DataRecorder对象处理,该对象可以记录各种来源数据并将它们写成表格式的文件。Enn声明仿真有关于如何使用DataRecorder对象的不错的例子,应该与这个文件联系在一起开发。处理DataRecorder类,其他一些数据收集的选项在这个文件的末尾进行了描述。

  DataRecorder的使用就是:创建它,给它添加数据源,然后安排记录写成文件。下面是一个创建DataRecorder变量记录器的例子,创建一个DataRecorder如下:recorder = new DataRecorder("./data.txt", this); 或者添加一个任选的头注释到输出文件:

recorder = new DataRecorder("./data.txt", this, "A Comment");

  写输出数据的文件的名字是第一个变量,第二个与记录器相关的模型(假定记录器在模型里建立,改变量为"this")。在第二个例子里,最后的变量是一个任选的头注释。一旦建立了DataRecorder,询问模型来决定仿真是否以分批模式处理。如果是这样,数据记录器将把全部数据写入一个文件并保持运行序号等的路径。

  建立和添加数据源到DataRecorder告知DataRecorder到哪里找到数据进行记录。通常根据安排机制,当收到record()信息时,DataRecorder将会记录来自这些源的数据。DataRecorder的数据源是一个针对一些对象的方法调用,或者是一些势力变量的值。实际的数据源可以分为两个类型:数字型和对象型。

  网络使DataRecorder知道如何和可能的多样类型的数据源工作,这些方法调用或者势力变量必须封装在NumericDataSource或DataSource两个接口中的一个,NumericDataSource适用于数字型数据源而DataSource适用于对象型。NumericDataSource 接口有一个专一的方法:public double execute();DataSource 接口也有一个专一的方法 public Object execute()。把你的数据源封装在这些接口中的某一个就得建立一个实现上述接口中的某一个的类,并且返回你需要从 execute 方法中记录的数据。好比BasicActions,你可以手动建立这些类,或者通过Repast动态创建。下面是一个例子:

public class MyModel : SimModelImpl
{
  private int numAgents;
  private Space space;
  private DataRecorder recorder;
	
  public int NumAgents
  {
    get
    {
      return numAgents;
    }
  }
	
  public String Space
  {
    get
    {
      return space;  
    }
  }
  
  ...
	
  class NumDataSource : NumericDataSouce
  {
    private MyModel enclosingInstance;
		
    public NumDataSource(MyModel enclosingInstance)
	{
      this.enclosingInstance = enclosingInstance;
	}
	public double execute()
	{
      return enclosingInstance.NumAgents;
	}
  }
	
  class ObjDataSource : DataSource
  {
    private MyModel enclosingInstance;
    public ObjDataSource(MyModel enclosingInstance)
    {
      this.enclosingInstance = enclosingInstance;
    }
    public Object execute()
    {
      return enclosingInstance.Space.Data;
    }
  }

  private void buildModel() {
    recorder = new DataRecorder("./data.txt", this);
    recorder.addNumericDataSource("numAgents", new NumDataSource());
    recorder.addObjectDataSource("SpaceData", new ObjDataSource());
    ...
  }
}

  

  这里,我们将两个数据源作为模型的内部类实现。第一个(NumDataSource)只是返回numAgents变量的数值;第二个(ObjDataSource)调用了一个基于某个假定的空间对象的方法,返回了一个对象来记录。然后,我们添加这些数据源到buildModel()方法中的记录器。添加到DataSources后,每当我们调用DataRecorder.record()时,这些DataSources的execute()方法将会被调用。(你也可以使用匿名的内部类…)

如果你希望Repast建立一个类来实现这些接口中的一个,编码如下:

public class MyModel : SimModelImpl
{

  private int numAgents;
  private Space space;
  private DataRecorder recorder;
  ...

  public int NumAgents
  {
    get { return numAgents; }
  }

  public String SpaceData
  {
    get { return space.Data; }
  }


  private void buildModel()
  {
    recorder = new DataRecorder("./data.txt", this);
    recorder.createNumericDataSource("numAgents", this, "getNumAgents");
    recorder.createObjectDataSource("SpaceData", this, "getSpaceData");
    ...
  }
}

  这里,我们建立DataSources,再添加调用DataRecorder对象的方法。create*方法为要记录的数据添加标签,一个参考对象,该对象调用的方法的名字返回该数据。所以,上例中Repast建立了一个NumericDataSource对象,它的execute方法调用getNumAgents()。ObjectDataSource的建立也是用类似方法。手动或动态建立数据源的方法的结果是差不多的。尽管如此,利用动态建立,诸如命名create*调用中的变量方法名字拼错一类的错误直至运行时才能发现。

对于NumericDataSources,你能指定小数点前或后你想要记录的位数。所以,例如recorder.createNumericDataSource("numAgents", this, "getNumAgents", 3, 4)将会记录小数点前 3 位和后 4 位。如果DataSource生成 3333.22222222,将会记录的数值是 333.2222。声明为-1 将记录所有位数,所以recorder.createNumericDataSource("numAgents", this, "getNumAgents", -1, 4)将会记录3333.2222。注意,这个数据没有被截去头,而是按照完全显示浮点数标准完全显示。

  由record()收集数据,并由writeToFile()把记录数据写入文件,这些步骤就像安排其他方法一样预设。

schedule.scheduleActionBeginning(0, new BasicAction() {
  public override void execute() {
    dRecorder.record();
  }
});
schedule.scheduleActionAtEnd(dRecorder, "writeToFile");

  上面的语句,告诉时刻安排机制针对DataRecorder变量dRecorder在0开始的每个节拍的调用record()方法,如上所述,dRecorder按上面说的方法询问他的数据源并记录结果。上句也告知安排机制对dRecorder调用"writeToFile"方法。然后,dRecorder会将收集到的数据写到dRecorder建立时指定的文件,如果在运行时已经写入,则把附载该数据。输出文件以为该运行设置模型参数的头文件开始,其后是表格形式的数据。  

RngSeed: 948816295132

numAgents: 100

maxAge: 120

"tick","Avg. Age"

0.0,33

1.0,33

2.0,32

3.0,40

...

  批处理中的输出文件有些不同,那里,任何联系的变量都从批处理的一开始就被记录在头文件中。批处理参数文件中定义的任何动态参数都被记录在表格中。你可以用 setDelimiter 方法改变记录数据的分隔符。缺省的分隔符是“,”所以缺省的记录数据和上边的相似。如果将分隔符变为“:”,结果如下

RngSeed: 948816295132

numAgents: 100

maxAge: 120

"tick":"Avg. Age"

0.0:33

1.0:33

2.0:32

3.0:40

...

  注意,内存中的数据直到调用writeToFile才被写入,如果你的内存有问题,将你的数据更频繁地写入文件。

Additional Ways to Collect Data

  处理DataRecorder,还有其他方法收集Repast仿真中的数据。记录网络数据的方法,参见Network Models。下面介绍通过ObjectDataRecorder记录数据。

  一个ObjectDataRecorder将任意对象以字符串记录到文件中,还有可选择的注释。典型地情况下,注释是节拍数,但也不一定。一个ObjectDataRecorder的建立很像带文件名的DataRecorder对象和相关的模型。与之不同的是,ObjectDataRecorder没有数据源,并且按要求通过它的record()方法记录任意数据。

  在不分批地模式下,写入的文件的开头包含模型属性和这些属性的值。这些值是第一次调用写入时的值,一些用户在设定后运行前调整这些参数。由此,你的初始模型参数应该只能由用户或参数文件改变。如果不是这样,文件的开头可能不准确。准确的数据将在快包含可选注释作为头文件的地方写入成块,其后是对象本身的字符串代号。对象的toString()方法需要该字符串代号。

  批处理模式下,任何连续的某些参数写在文件开头。数据块包含有任何动态参数的值,运行号和可选注释(同样,经常是节拍数),还有记录对象的字符串代号。

  数据通过ObjectDataRecorder的record()方法记录,write()方法写入文件。详见API docs。

posted @ 2014-12-25 15:04  Buller Lee  阅读(774)  评论(0)    收藏  举报