Class: RedAmber::SubFrames
- Inherits:
-
Object
- Object
- RedAmber::SubFrames
- Includes:
- Enumerable, Helper
- Defined in:
- lib/red_amber/subframes.rb
Overview
class SubFrames treats subsets of a DataFrame
- Experimental feature
-
Class SubFrames may be removed or be changed in the future.
Class Method Summary collapse
-
.by_dataframes(dataframes) ⇒ SubFrames
private
Create a new SubFrames from an Array of DataFrames.
-
.by_filters(dataframe, subset_filters) ⇒ SubFrames
private
Create a new SubFrames object from a DataFrame and an array of filters.
-
.by_group(group) ⇒ SubFrames
Create SubFrames from a Group.
-
.by_indices(dataframe, subset_indices) ⇒ SubFrames
private
Create a new SubFrames object from a DataFrame and an array of indices.
Instance Method Summary collapse
-
#aggregate(*args, &block) ⇒ Object
Aggregate SubFrames to create a DataFrame.
-
#assign ⇒ Object
Update existing column(s) or create new columns(s) for each DataFrames in self.
-
#baseframe ⇒ DataFrame
(also: #concatenate, #concat)
Return concatenated SubFrames as a DataFrame.
-
#each(&block) ⇒ Object
Iterates over sub DataFrames or returns an Enumerator.
-
#empty? ⇒ true, false
Test if subset is empty?.
-
#filter_map {|dataframe| ... } ⇒ SubFrames
Returns a SubFrames containing truthy DataFrames returned by the block.
-
#frames(n_frames = nil) ⇒ Object
Return an Array of sub DataFrames.
-
#initialize(dataframe, selectors = nil, &block) ⇒ SubFrames
constructor
Create a new SubFrames object from a DataFrame and an array of indices or filters.
-
#inspect(limit: 5) ⇒ String
Return summary information of self.
-
#map {|dataframe| ... } ⇒ SubFrames
(also: #collect)
Returns a SubFrames containing DataFrames returned by the block.
-
#offset_indices ⇒ Array<Integer>
Indices at the top of each sub DataFrames.
-
#reject {|dataframe| ... } ⇒ SubFrames
Returns a SubFrames containing DataFrames rejected by the block.
-
#select {|dataframe| ... } ⇒ SubFrames
(also: #filter, #find_all)
Returns a SubFrames containing DataFrames selected by the block.
-
#size ⇒ Integer
Number of subsets.
-
#sizes ⇒ Array<Integer>
Size list of subsets.
-
#take(num) ⇒ SubFrames
Return 0…num sub-dataframes in self.
-
#to_s(limit: 5) ⇒ String
Return string representation of self.
-
#universal? ⇒ true, false
Test if self has only one subset and it is comprehensive.
Constructor Details
#initialize(dataframe, subset_specifier) ⇒ SubFrames #initialize(dataframe) {|dataframe| ... } ⇒ SubFrames
Create a new SubFrames object from a DataFrame and an array of indices or filters.
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/red_amber/subframes.rb', line 288 def initialize(dataframe, selectors = nil, &block) unless dataframe.is_a?(DataFrame) raise SubFramesArgumentError, "not a DataFrame: #{dataframe}" end if block unless selectors.nil? raise SubFramesArgumentError, 'Must not specify both arguments and block.' end selectors = yield(dataframe) end if dataframe.empty? || selectors.nil? || selectors.size.zero? # rubocop:disable Style/ZeroLengthPredicate @baseframe = DataFrame.new @selectors = Selectors.new([]) else @baseframe = dataframe @selectors = if selectors.first.boolean? Filters.new(selectors) elsif selectors.first.numeric? Indices.new(selectors) else raise SubFramesArgumentError, "illegal type: #{selectors}" end end @frames = [] end |
Class Method Details
.by_dataframes(dataframes) ⇒ SubFrames
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
dataframes must have same schema.
Create a new SubFrames from an Array of DataFrames.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/red_amber/subframes.rb', line 157 def by_dataframes(dataframes) instance = allocate case Array(dataframes) when [] || [nil] instance.instance_variable_set(:@baseframe, DataFrame.new) instance.instance_variable_set(:@selectors, []) instance.instance_variable_set(:@frames, []) else instance.instance_variable_set(:@baseframe, nil) instance.instance_variable_set(:@selectors, nil) instance.instance_variable_set(:@frames, dataframes) end instance end |
.by_filters(dataframe, subset_filters) ⇒ SubFrames
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
this method doesn’t check arguments.
Create a new SubFrames object from a DataFrame and an array of filters.
139 140 141 142 143 144 145 |
# File 'lib/red_amber/subframes.rb', line 139 def by_filters(dataframe, subset_filters) instance = allocate instance.instance_variable_set(:@baseframe, dataframe) instance.instance_variable_set(:@selectors, Filters.new(subset_filters)) instance.instance_variable_set(:@frames, []) instance end |
.by_group(group) ⇒ SubFrames
Create SubFrames from a Group.
- Experimental feature
-
this method may be removed or be changed in the future.
102 103 104 |
# File 'lib/red_amber/subframes.rb', line 102 def by_group(group) SubFrames.by_filters(group.dataframe, group.filters) end |
.by_indices(dataframe, subset_indices) ⇒ SubFrames
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
this method doesn’t check arguments.
Create a new SubFrames object from a DataFrame and an array of indices.
118 119 120 121 122 123 124 |
# File 'lib/red_amber/subframes.rb', line 118 def by_indices(dataframe, subset_indices) instance = allocate instance.instance_variable_set(:@baseframe, dataframe) instance.instance_variable_set(:@selectors, Indices.new(subset_indices)) instance.instance_variable_set(:@frames, []) instance end |
Instance Method Details
#aggregate(keys) {|dataframe| ... } ⇒ DataFrame #aggregate {|dataframe| ... } ⇒ DataFrame #aggregate {|dataframe| ... } ⇒ DataFrame #aggregate(group_keys, aggregations) ⇒ DataFrame #aggregate(group_keys, aggregations) ⇒ DataFrame
This method does not check if aggregation function is used.
Aggregate SubFrames to create a DataFrame.
This method creates a DataFrame with one row corresponding to one sub dataframe.
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
# File 'lib/red_amber/subframes.rb', line 561 def aggregate(*args, &block) aggregator = if block if args.empty? # aggregate { {key => value} or [[key, value], ...] } each_with_object(Hash.new { |h, k| h[k] = [] }) do |df, hash| df.instance_eval(&block).to_h.each do |k, v| hash[k] << v end end else # aggregate(keys) { values } values = each.map { |df| Array(df.instance_eval(&block)) }.transpose args.flatten.zip(values) end else # These functions may be removed in the future. case args in [group_keys1, Hash => h] # aggregate(group_keys, { key => func }) ary = Array(group_keys1).map { |key| [:first, key] } ary.concat(h.to_a.map { [_2, _1] }) # rubocop:disable Style/NumberedParametersLimit in [group_keys2, [Array => keys, Array => funcs]] # aggregate(group_keys, [keys, funcs]) ary = Array(group_keys2).map { |key| [:first, key] } ary.concat(funcs.product(keys)) else raise SubFramesArgumentError, "invalid argument: #{args}" end sf = self ary.map do |func, key| label = func == :first ? key : "#{func}_#{key}" [label, sf.each.map { |df| df[key].send(func) }] end end DataFrame.new(aggregator) end |
#assign(key) {|dataframe| ... } ⇒ SubFrames #assign(keys) {|dataframe| ... } ⇒ SubFrames #assign {|dataframe| ... } ⇒ SubFrames
Update existing column(s) or create new columns(s) for each DataFrames in self.
Column values are updated by an oveloaded common operation.
780 781 782 |
# File 'lib/red_amber/subframes.rb', line 780 def assign(...) map { |df| df.assign(...) } end |
#baseframe ⇒ DataFrame Also known as: concatenate, concat
Return concatenated SubFrames as a DataFrame.
Once evaluated, memorize it as @baseframe.
325 326 327 |
# File 'lib/red_amber/subframes.rb', line 325 def baseframe @baseframe ||= reduce(&:concatenate) end |
#each ⇒ Enumerator #each {|subframe| ... } ⇒ self
Iterates over sub DataFrames or returns an Enumerator.
This method will memorize sub DataFrames and always returns the same object. The Class SubFrames is including Enumerable module. So many methods in Enumerable are available.
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
# File 'lib/red_amber/subframes.rb', line 398 def each(&block) return enum_for(__method__) { size } unless block if @selectors @selectors.each.with_index do |selector, i| if i < @frames.size yield @frames[i] else frame = get_subframe(selector) @frames << frame yield frame end end else @frames.each(&block) end nil end |
#empty? ⇒ true, false
Test if subset is empty?.
1024 1025 1026 |
# File 'lib/red_amber/subframes.rb', line 1024 def empty? size.zero? end |
#filter_map {|dataframe| ... } ⇒ SubFrames
Returns a SubFrames containing truthy DataFrames returned by the block.
With a block given, calls the block with successive DataFrames; returns a SubFrames of those DataFrames for which the block returns nil or false.
- Returns SubFrames
-
Use ‘#each.filter_map` if you want to get DataFrames by Array.
Returns an Enumerator with no block given.
943 |
# File 'lib/red_amber/subframes.rb', line 943 define_subframable_method :filter_map |
#frames ⇒ Array<DataFrame> #frames(n_frames) ⇒ Array<DataFrame>
Return an Array of sub DataFrames
1163 1164 1165 1166 1167 1168 1169 1170 1171 |
# File 'lib/red_amber/subframes.rb', line 1163 def frames(n_frames = nil) n_frames = size if n_frames.nil? if @frames.size < n_frames @frames = each.take(n_frames) else @frames.take(n_frames) end end |
#inspect(limit: 5) ⇒ String
Return summary information of self.
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 |
# File 'lib/red_amber/subframes.rb', line 1130 def inspect(limit: 5) shape = if @baseframe.nil? '(Not prepared)' else baseframe.shape_str(with_id: true) end sizes_truncated = (size > limit ? sizes.take(limit) << '...' : sizes).join(', ') "#<#{self.class} : #{format('0x%016x', object_id)}>\n" \ "@baseframe=#<#{shape}>\n" \ "#{size} SubFrame#{pl(size)}: " \ "[#{sizes_truncated}] in size#{pl(size)}.\n" \ "---\n#{_to_s(limit: limit, with_id: true)}" end |
#map {|dataframe| ... } ⇒ SubFrames Also known as: collect
Returns a SubFrames containing DataFrames returned by the block.
- Returns SubFrames
-
Use ‘#each.map` if you want to get DataFrames by Array.
Returns an Enumerator with no block given.
657 |
# File 'lib/red_amber/subframes.rb', line 657 define_subframable_method :map |
#offset_indices ⇒ Array<Integer>
Indices at the top of each sub DataFrames.
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 |
# File 'lib/red_amber/subframes.rb', line 1003 def offset_indices case @selectors when Filters @selectors.selectors.map do |selector| selector.each.with_index.find { |x, _| x }[1] end else # Indices, nil sum = 0 sizes.map do |size| sum += size sum - size end end end |
#reject {|dataframe| ... } ⇒ SubFrames
Returns a SubFrames containing DataFrames rejected by the block.
With a block given, calls the block with successive DataFrames; returns a SubFrames of those DataFrames for which the block returns nil or false.
- Returns SubFrames
-
Use ‘#each.reject` if you want to get DataFrames by Array.
Returns an Enumerator with no block given.
907 |
# File 'lib/red_amber/subframes.rb', line 907 define_subframable_method :reject |
#select {|dataframe| ... } ⇒ SubFrames Also known as: filter, find_all
Returns a SubFrames containing DataFrames selected by the block.
With a block given, calls the block with successive DataFrames; returns a SubFrames of those DataFrames for which the block returns a truthy value.
- Returns SubFrames
-
Use ‘#each.select` if you want to get DataFrames by Array.
Returns an Enumerator with no block given.
848 |
# File 'lib/red_amber/subframes.rb', line 848 define_subframable_method :select |
#size ⇒ Integer
Number of subsets.
971 972 973 974 975 976 977 978 |
# File 'lib/red_amber/subframes.rb', line 971 def size @size ||= if @selectors @selectors.size else @frames.size end end |
#sizes ⇒ Array<Integer>
Size list of subsets.
986 987 988 989 990 991 992 993 |
# File 'lib/red_amber/subframes.rb', line 986 def sizes @sizes ||= if @selectors @selectors.sizes else @frames.map(&:size) end end |
#take(num) ⇒ SubFrames
Return 0…num sub-dataframes in self.
955 956 957 958 959 960 961 962 963 |
# File 'lib/red_amber/subframes.rb', line 955 def take(num) if num.zero? SubFrames.new(DataFrame.new, []) elsif num >= size self else SubFrames.by_dataframes(frames(num)) end end |
#to_s(limit: 5) ⇒ String
Return string representation of self.
1078 1079 1080 |
# File 'lib/red_amber/subframes.rb', line 1078 def to_s(limit: 5) _to_s(limit: limit) end |
#universal? ⇒ true, false
Test if self has only one subset and it is comprehensive.
1034 1035 1036 |
# File 'lib/red_amber/subframes.rb', line 1034 def universal? size == 1 && first == @baseframe end |