Class: YPetri::Net::DataSet
- Inherits:
-
Hash
- Object
- Hash
- YPetri::Net::DataSet
- Defined in:
- lib/y_petri/net/data_set.rb
Overview
YPetri::Net::DataSet
is a collection of labeled state records. It is a subclass of Hash
. DataSet
keys are known as events, and values are data points. Each of these data points is an array that can be used to reconstruct a record, (an object of YPetri::Net::State::Features::Record
class). Each record records a specific feature set (of YPetri::Net::State::Features
class). DataSet
class is intended to be parametrized with such specific feature set, which indicates the meaning of its data points.
Apart from methods inherited from Hash
, DataSet
can load a record at a given event (#record
method), reconstruct a simulation at a given event (#reconstruct
method), return columns corresponding to features (#series
method) and perform feature selection (#marking
, #firing
, #flux
, #gradient
, #delta
, #assignment
, and #reduced_features
for mixed feature sets). Apart from standard inspection methods, DataSet
has methods #print
and #plot
for visual presentation. Also, DataSet
has methods specially geared towards records of timed simulations, whose events are points in time. Method #interpolate
uses linear interpolation to find the approximate state of the system at some exact time using linear interpolation between the nearest earlier and later data points (which can be accessed respectively by #floor
and #ceiling
methods). Interpolation is available to the user, and is also used by DataSet#resample method for resampling the dataset.
Finally, it is possible that especially professional statisticians have written, or are planning to write, a DataSet
class better than this one. If I discover a good DataSet
class in the future, I would like to inherit from it or otherwise integrate with it for the purposes of DataSet
.
Instance Attribute Summary collapse
-
#settings ⇒ Object
readonly
TODO: More like event_type, idea not matured yet.
-
#type ⇒ Object
readonly
TODO: More like event_type, idea not matured yet.
Class Method Summary collapse
Instance Method Summary collapse
-
#Assignment(array) ⇒ Object
Expects an array of assignment feature identifiers.
-
#assignment(*ids) ⇒ Object
Expects an arbitrary number of assignment feature identifiers as arguments, and returns a subset of this dataset with only the specified assignment features retained.
-
#ceiling(event, equal_ok = true) ⇒ Object
Returns the nearest event greater or equal to the supplied event-type argument.
-
#Delta(array, transitions: nil, **named_args) ⇒ Object
Expects an array of delta feature identifiers, optionally qualified by the
:transitions
named argument, defaulting to all the transitions in the net. -
#delta(*ordered_args, transitions: nil, **named_args) ⇒ Object
Expects an arbitrary number of ordered arguments identifying delta features, optionally qualified by the
:transitions
named argument, defaulting to all the transitions in the net. - #delta_timed(*ordered_args, **named_args) ⇒ Object
- #delta_timeless(*ordered_args, **named_args) ⇒ Object
-
#distance(other) ⇒ Object
Computes the distance to another dataset.
-
#firing(*ids, **named_args) ⇒ Object
Expects an arbitrary number of firing feature identifiers and returns a subset of this dataset with only the specified firing features retained.
-
#Firing(array, **named_args) ⇒ Object
Expects an array of firing feature identifiers, and returns a subset of this dataset with only the specified firing features retained.
-
#floor(event, equal_ok = true) ⇒ Object
Returns the nearest event smaller or equal to the supplied event-type argument.
-
#Flux(array) ⇒ Object
Expects an array of flux feature identifiers, and returns a subset of this dataset with only the specified flux features retained.
-
#flux(*ids) ⇒ Object
Expects an arbitrary number of flux feature identifiers, and returns a subset of this dataset, with only the specified flux features retained.
-
#Gradient(array, transitions: nil) ⇒ Object
Expects an array of gradient feature identifiers, optionally qualified by the
:transitions
named argument, defaulting to all T transitions in the net. -
#gradient(*ids, transitions: nil) ⇒ Object
Returns a subset of this dataset with only the specified gradient features identified by the arguments retained.
-
#inspect ⇒ Object
Inspect string of the instance.
-
#interpolate(event) ⇒ Object
(also: #at)
Interpolates the recording at the given point (event).
-
#Marking(array) ⇒ Object
Expects an array of marking feature identifiers, and returns a subset of this dataset with only the specified marking features retained.
-
#marking(*ids) ⇒ Object
Expects an arbitrary number of marking feature identifiers, and returns a subset of this dataset with only the specified marking features retained.
-
#plot(nodes = nil, except: [], **named_args) ⇒ Object
Plots the dataset using Ruby Gnuplot gem.
-
#print(precision: 4, distance: precision + 4) ⇒ Object
Pretty print the dataset.
-
#reconstruct(at: (fail "No event given!"), **settings) ⇒ Object
Recreates the simulation at a given event label.
-
#record(event) ⇒ Object
Returns the Record instance corresponding to the given recorded event.
-
#records ⇒ Object
Revives records from values.
-
#reduce_features(array = nil, **named_args) ⇒ Object
Expects a hash of features (:marking (alias :state) of places, :firing of tS transitions, :delta of places and/or transitions, :assignment of A transitions) and returns the corresponding mapping of the recording.
-
#resample(**settings) ⇒ Object
Resamples the recording.
-
#series(array = nil) ⇒ Object
Returns the data series for the specified features.
-
#timed? ⇒ Boolean
Type of the dataset.
-
#to_csv ⇒ Object
Outputs the current recording in CSV format.
-
#to_s ⇒ Object
Returns a string briefly discribing the dataset.
Instance Attribute Details
#settings ⇒ Object (readonly)
TODO: More like event_type, idea not matured yet.
77 78 79 |
# File 'lib/y_petri/net/data_set.rb', line 77 def settings @settings end |
#type ⇒ Object (readonly)
TODO: More like event_type, idea not matured yet.
77 78 79 |
# File 'lib/y_petri/net/data_set.rb', line 77 def type @type end |
Class Method Details
.__new__ ⇒ Object
54 |
# File 'lib/y_petri/net/data_set.rb', line 54 alias __new__ new |
.new(type: nil) ⇒ Object
56 57 58 59 60 61 62 63 64 |
# File 'lib/y_petri/net/data_set.rb', line 56 def new type: nil __new__ do |hsh, missing| case missing when Float then nil else hsh[ missing.to_f ] end end.tap { |inst| inst.instance_variable_set :@type, type } end |
Instance Method Details
#Assignment(array) ⇒ Object
Expects an array of assignment feature identifiers. Returns a subset of this dataset with only the specified assignment features retained.
346 347 348 |
# File 'lib/y_petri/net/data_set.rb', line 346 def Assignment array reduce_features assignment: array end |
#assignment(*ids) ⇒ Object
Expects an arbitrary number of assignment feature identifiers as arguments, and returns a subset of this dataset with only the specified assignment features retained. If no arguments are given, all the assignment features are assumed.
355 356 357 358 |
# File 'lib/y_petri/net/data_set.rb', line 355 def assignment *ids return reduce_features net.State.Features.assignment if args.empty? reduce_features assignment: ids end |
#ceiling(event, equal_ok = true) ⇒ Object
Returns the nearest event greater or equal to the supplied event-type argument. The second optional ordered argument, true by default, controls whether equality is accepted. If set to false, then the nearest greater event is sought.
107 108 109 110 |
# File 'lib/y_petri/net/data_set.rb', line 107 def ceiling( event, equal_ok=true ) e = events.ascending_ceiling( event, equal_ok ) e.nil? ? nil : e end |
#Delta(array, transitions: nil, **named_args) ⇒ Object
Expects an array of delta feature identifiers, optionally qualified by the :transitions
named argument, defaulting to all the transitions in the net.
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/y_petri/net/data_set.rb', line 306 def Delta array, transitions: nil, **named_args if named_args.has? :delta_time, syn!: :Δt then Δt = named_args.delete( :delta_time ) if transitions.nil? then reduce_features delta: array, Δt: Δt else reduce_features delta: [ *array, transitions: transitions ], Δt: Δt end else if transitions.nil? then reduce_features delta: array else reduce_features delta: [ *array, transitions: transitions ] end end end |
#delta(*ordered_args, transitions: nil, **named_args) ⇒ Object
Expects an arbitrary number of ordered arguments identifying delta features, optionally qualified by the :transitions
named argument, defaulting to all the transitions in the net.
327 328 329 330 331 332 |
# File 'lib/y_petri/net/data_set.rb', line 327 def delta *ordered_args, transitions: nil, **named_args return Delta( ordered_args, transitions: transitions, **named_args ) unless ordered_args.empty? return Delta( net.places, **named_args ) if transitions.nil? Delta( net.places, transitions: transitions, **named_args ) end |
#delta_timed(*ordered_args, **named_args) ⇒ Object
335 336 337 |
# File 'lib/y_petri/net/data_set.rb', line 335 def delta_timed *ordered_args, **named_args delta *ordered_args, transitions: net.T_transitions, **named_args end |
#delta_timeless(*ordered_args, **named_args) ⇒ Object
339 340 341 |
# File 'lib/y_petri/net/data_set.rb', line 339 def delta_timeless *ordered_args, **named_args delta *ordered_args, transitions: net.t_transitions, **named_args end |
#distance(other) ⇒ Object
Computes the distance to another dataset.
171 172 173 174 175 176 177 178 |
# File 'lib/y_petri/net/data_set.rb', line 171 def distance( other ) sum_of_sq = events .map { |e| [ e, other.interpolate( e ) ] } .map { |rec1, rec2| rec1.euclidean_distance rec2 } .map { |dist| dist * dist } .reduce( :+ ) sum_of_sq ** 0.5 end |
#firing(*ids, **named_args) ⇒ Object
Expects an arbitrary number of firing feature identifiers and returns a subset of this dataset with only the specified firing features retained. Named arguments may include :delta_time
, alias :Δt (for firing of timed transitions).
259 260 261 262 |
# File 'lib/y_petri/net/data_set.rb', line 259 def firing *ids, **named_args return Firing net.State.Features.firing, **named_args if ids.empty? Firing ids, **named_args end |
#Firing(array, **named_args) ⇒ Object
Expects an array of firing feature identifiers, and returns a subset of this dataset with only the specified firing features retained. Named arguments may include :delta_time
, alias :Δt (for firing of timed transitions).
250 251 252 |
# File 'lib/y_petri/net/data_set.rb', line 250 def Firing array, **named_args reduce_features firing: array, **named_args end |
#floor(event, equal_ok = true) ⇒ Object
Returns the nearest event smaller or equal to the supplied event-type argument. The second optional ordered argument, true by default, controls whether equality is accepted. If set to false, then the nearest smaller event is sought.
97 98 99 100 |
# File 'lib/y_petri/net/data_set.rb', line 97 def floor( event, equal_ok=true ) e = events.ascending_floor( event, equal_ok ) e.nil? ? nil : e end |
#Flux(array) ⇒ Object
Expects an array of flux feature identifiers, and returns a subset of this dataset with only the specified flux features retained.
267 268 269 |
# File 'lib/y_petri/net/data_set.rb', line 267 def Flux array reduce_features flux: array end |
#flux(*ids) ⇒ Object
Expects an arbitrary number of flux feature identifiers, and returns a subset of this dataset, with only the specified flux features retained. If no aruments are given, full set of flux features is assumed.
275 276 277 278 |
# File 'lib/y_petri/net/data_set.rb', line 275 def flux *ids return Flux net.State.Features.flux if ids.empty? Flux ids end |
#Gradient(array, transitions: nil) ⇒ Object
Expects an array of gradient feature identifiers, optionally qualified by the :transitions
named argument, defaulting to all T transitions in the net.
284 285 286 287 288 289 290 |
# File 'lib/y_petri/net/data_set.rb', line 284 def Gradient array, transitions: nil if transitions.nil? then reduce_features gradient: array else reduce_features gradient: [ *array, transitions: transitions ] end end |
#gradient(*ids, transitions: nil) ⇒ Object
Returns a subset of this dataset with only the specified gradient features identified by the arguments retained. If no arguments are given, all the gradient features from the receiver dataset are selected.
296 297 298 299 300 |
# File 'lib/y_petri/net/data_set.rb', line 296 def gradient *ids, transitions: nil return Gradient net.State.Features.gradient, transitions: transitions if ids.empty? Gradient ids, transitions: transitions end |
#inspect ⇒ Object
Inspect string of the instance.
468 469 470 |
# File 'lib/y_petri/net/data_set.rb', line 468 def inspect to_s end |
#interpolate(event) ⇒ Object Also known as: at
Interpolates the recording at the given point (event). Return value is the Record class instance.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/y_petri/net/data_set.rb', line 134 def interpolate( event ) begin record( event ) rescue KeyError => msg timed? or raise TypeError, "Event #{event} not recorded! (%s)" % "simulation type: #{type.nil? ? 'nil' : type}" # (Remark: #floor, #ceiling supported by timed datasets only) floor = floor( event ) fail "Event #{event} has no floor!" if floor.nil? fl = Matrix.column_vector record( floor ) ceiling = ceiling( event ) fail "Event #{event} has no ceiling!" if ceiling.nil? ce = Matrix.column_vector record( ceiling ) rslt = fl + ( ce - fl ) / ( ceiling - floor ) * ( event - floor ) features.load( rslt.column_to_a ) end end |
#Marking(array) ⇒ Object
Expects an array of marking feature identifiers, and returns a subset of this dataset with only the specified marking features retained.
232 233 234 |
# File 'lib/y_petri/net/data_set.rb', line 232 def Marking array reduce_features marking: array end |
#marking(*ids) ⇒ Object
Expects an arbitrary number of marking feature identifiers, and returns a subset of this dataset with only the specified marking features retained. If no arguments are given, all the marking features are assumed.
240 241 242 243 |
# File 'lib/y_petri/net/data_set.rb', line 240 def marking *ids return Marking net.State.Features.marking if ids.empty? Marking ids end |
#plot(nodes = nil, except: [], **named_args) ⇒ Object
Plots the dataset using Ruby Gnuplot gem. Takes several optional arguments: The list of nodes to plot can be supplied as optional first ordered argument. If supplied, the nodes are converted into features using Net::State::Features.infer_from_nodes
method. Similarly, the features to exclude can be specified as a list of nodes supplied under except:
parameter. Under except:
parameter, it is also possible to supply a feature-specifying hash. Otherwise, feature specification can be passed to the method via named arguments. By default, it is assumed that the caller means to plot all the features of this dataset.
Named arguments admit Gnuplot keywords that control the plot. These parameters include title:
, xlabel:
and ylabel:
.
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
# File 'lib/y_petri/net/data_set.rb', line 385 def plot( nodes=nil, except: [], **named_args ) nn = named_args time = nn.may_have :time, syn!: :time_range events = events() # Figure out the features to plot. ff = if nodes.nil? then nn_ff = nn.slice [ :marking, :flux, :firing, :gradient, :delta, :assignment ] nn_ff.empty? ? features : net.State.Features( nn_ff ) else net.State.Features.infer_from_nodes( nodes ) end # Figure out the features to exclude from plotting # ("except" features). xff = case except when Array then net.State.Features.infer_from_nodes( except ) when Hash then net.State.Features( except ) else fail TypeError, "Wrong type of :except argument: #{except.class}" end # Subtract the "except" features from features to plot. ff -= xff # Convert the feature set into a set of data arrays. data_arrays = series( ff ) # Figure out the x axis range for plotting. x_range = if nn.has? :time then if time.is_a? Range then "[#{time.begin}:#{time.end}]" else "[-0:#{SY::Time.magnitude( time ).amount rescue time}]" end else from = events.first || 0 to = if events.last and events.last > from then events.last else events.first + 1 end "[#{from}:#{to}]" end # Invoke Gnuplot. Gnuplot.open do |gp| Gnuplot::Plot.new gp do |plot| plot.xrange x_range if nn.has? :yrange, syn!: :y_range then if nn[:yrange].is_a? Range then plot.yrange "[#{nn[:yrange].begin}:#{nn[:yrange].end}]" else fail TypeError, "Argument :yrange is not a range!" end end plot.title nn[:title] || "#{net} plot" plot.ylabel nn[:ylabel] || "Values" plot.xlabel nn[:xlabel] || "Time [s]" ff.labels.zip( data_arrays ).each do |label, array| # Replace NaN and Infinity with 0.0 and warn about it. nan, inf = 0, 0 array = array.map { |v| if v.to_f.infinite? then inf += 1; 0.0 elsif v.to_f.nan? then nan += 1; 0.0 else v end } # Warn. nan = nan > 0 ? "#{nan} NaN values" : nil inf = inf > 0 ? "#{inf} infinite values" : nil msg = "Warning: column #{label} contains %s plotted as 0!" warn msg % [ nan, inf ].compact.join( ' and ' ) if nan or inf # Finally, plot. plot.data << Gnuplot::DataSet.new( [ events, array ] ) { |set| set.with = "linespoints" set.title = label } end end end end |
#print(precision: 4, distance: precision + 4) ⇒ Object
Pretty print the dataset. Takes :precision
and :distance
named arguments, that control the shape of the printed table.
475 476 477 478 479 480 481 482 |
# File 'lib/y_petri/net/data_set.rb', line 475 def print precision: 4, distance: precision + 4 features.labels.print_as_line precision: precision, distance: distance puts '-' * features.size * distance records.each { |record| record.print_as_line precision: precision, distance: distance } return nil end |
#reconstruct(at: (fail "No event given!"), **settings) ⇒ Object
Recreates the simulation at a given event label.
120 121 122 123 124 125 126 127 128 129 |
# File 'lib/y_petri/net/data_set.rb', line 120 def reconstruct at: (fail "No event given!"), **settings # settings may include marking clamps, marking, inital marking... record = interpolate( at ) settings = settings().merge settings if settings() if timed? then record.reconstruct time: at, **settings else record.reconstruct **settings end end |
#record(event) ⇒ Object
Returns the Record instance corresponding to the given recorded event.
88 89 90 |
# File 'lib/y_petri/net/data_set.rb', line 88 def record( event ) features.load( fetch event ) end |
#records ⇒ Object
Revives records from values.
114 115 116 |
# File 'lib/y_petri/net/data_set.rb', line 114 def records values.map { |value| features.Record.new( value ) } end |
#reduce_features(array = nil, **named_args) ⇒ Object
Expects a hash of features (:marking (alias :state) of places, :firing of tS transitions, :delta of places and/or transitions, :assignment of A transitions) and returns the corresponding mapping of the recording.
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/y_petri/net/data_set.rb', line 191 def reduce_features array=nil, **named_args delta_time_given = named_args.has? :delta_time, syn!: :Δt Δt = named_args.delete :delta_time ff = net.State.Features[ *array, **named_args ] # reduced feature set absent = ff - features() # features absent from the current set present = ff - absent # features present in the current set timedness = true if absent.any? { |f| f.timed? rescue false } fail ArgumentError, "Reconstruction of timed features requires Δt to be" + "supplied!" unless delta_time_given if timedness present_ii = present.each_with_object( {} ) { |f, ꜧ| ꜧ[f] = features().index f } ds = ff.DataSet.new type: type if absent.empty? then # no reconstruction ( events >> records ).each_with_object ds do |(event, record), dataset| line = record.values_at *ff.map( &present_ii.method( :[] ) ) dataset.update event => ff.load( line ) end else ( events >> records ).each_with_object ds do |(event, record), dataset| reconstructed_sim = reconstruct at: event line = if timedness then ff.map { |f| i = present_ii[ f ] break record[ i ] if i f.extract_from( reconstructed_sim, Δt: Δt ) } else ff.map { |f| i = present_ii[ f ] break record[ i ] if i f.extract_from( reconstructed_sim, Δt: Δt ) } end dataset.update event => ff.load( line ) end end end |
#resample(**settings) ⇒ Object
Resamples the recording.
155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/y_petri/net/data_set.rb', line 155 def resample **settings time_range = settings.may_have( :time_range, syn!: :time ) || events.first .. events.last sampling = settings.must_have :sampling t0, target_time = time_range.begin, time_range.end t = t0 o = self.class.new type: type loop do o.update t => interpolate( t ) t += sampling return o if t > target_time end end |
#series(array = nil) ⇒ Object
Returns the data series for the specified features.
182 183 184 185 |
# File 'lib/y_petri/net/data_set.rb', line 182 def series array=nil return records.transpose if array.nil? reduce_features( net.State.Features array ).series end |
#timed? ⇒ Boolean
Type of the dataset.
82 83 84 |
# File 'lib/y_petri/net/data_set.rb', line 82 def timed? type == :timed end |
#to_csv ⇒ Object
Outputs the current recording in CSV format.
362 363 364 365 366 |
# File 'lib/y_petri/net/data_set.rb', line 362 def to_csv require 'csv' [ ":event", *features.labels.map( &:to_s ) ].join( ',' ) + "\n" + map { |lbl, rec| [ lbl, *rec ].join ',' }.join( "\n" ) end |
#to_s ⇒ Object
Returns a string briefly discribing the dataset.
459 460 461 462 463 464 |
# File 'lib/y_petri/net/data_set.rb', line 459 def to_s "#<DataSet: " + "#{keys.size} records, " + "features: #{features}" + ">" end |