lib/fat_table/column.rb in fat_table-0.5.4 vs lib/fat_table/column.rb in fat_table-0.5.5
- old
+ new
@@ -189,12 +189,15 @@
avg var pvar dev pdev
any? all? none? one?)
# :category: Aggregates
- # Return the first non-nil item in the Column. Works with any Column type.
+ # Return the first non-nil item in the Column, or nil if all items are
+ # nil. Works with any Column type.
def first
+ return nil if items.all?(&:nil?)
+
if type == 'String'
items.reject(&:blank?).first
else
items.filter_to_type(type).first
end
@@ -202,33 +205,37 @@
# :category: Aggregates
# Return the last non-nil item in the Column. Works with any Column type.
def last
+ return nil if items.all?(&:nil?)
+
if type == 'String'
items.reject(&:blank?).last
else
items.filter_to_type(type).last
end
end
# :category: Aggregates
- # Return a count of the non-nil items in the Column. Works with any Column
- # type.
+ # Return a count of the non-nil items in the Column, or the size of the
+ # column if all items are nil. Works with any Column type.
def count
+ return items.size if items.all?(&:nil?)
+
if type == 'String'
items.reject(&:blank?).count.to_d
else
items.filter_to_type(type).count.to_d
end
end
# :category: Aggregates
- # Return the smallest non-nil, non-blank item in the Column. Works with
- # numeric, string, and datetime Columns.
+ # Return the smallest non-nil, non-blank item in the Column, or nil if all
+ # items are nil. Works with numeric, string, and datetime Columns.
def min
only_with('min', 'NilClass', 'Numeric', 'String', 'DateTime')
if type == 'String'
items.reject(&:blank?).min
else
@@ -236,12 +243,12 @@
end
end
# :category: Aggregates
- # Return the largest non-nil, non-blank item in the Column. Works with
- # numeric, string, and datetime Columns.
+ # Return the largest non-nil, non-blank item in the Column, or nil if all
+ # items are nil. Works with numeric, string, and datetime Columns.
def max
only_with('max', 'NilClass', 'Numeric', 'String', 'DateTime')
if type == 'String'
items.reject(&:blank?).max
else
@@ -249,38 +256,45 @@
end
end
# :category: Aggregates
- # Return a Range object for the smallest to largest value in the column.
- # Works with numeric, string, and datetime Columns.
+ # Return a Range object for the smallest to largest value in the column,
+ # or nil if all items are nil. Works with numeric, string, and datetime
+ # Columns.
def range
only_with('range', 'NilClass', 'Numeric', 'String', 'DateTime')
+ return nil if items.all?(&:nil?)
+
Range.new(min, max)
end
# :category: Aggregates
- # Return the sum of the non-nil items in the Column. Works with numeric and
- # string Columns. For a string Column, it will return the concatenation of
- # the non-nil items.
+ # Return the sum of the non-nil items in the Column, or 0 if all items are
+ # nil. Works with numeric and string Columns. For a string Column, it
+ # will return the concatenation of the non-nil items.
def sum
+ return 0 if type == 'NilClass' || items.all?(&:nil?)
+
only_with('sum', 'Numeric', 'String')
if type == 'String'
items.reject(&:blank?).join(' ')
else
items.filter_to_type(type).sum
end
end
# :category: Aggregates
- # Return the average value of the non-nil items in the Column. Works with
- # numeric and datetime Columns. For datetime Columns, it converts each date
- # to its Julian day number, computes the average, and then converts the
- # average back to a DateTime.
+ # Return the average value of the non-nil items in the Column, or 0 if all
+ # items are nil. Works with numeric and datetime Columns. For datetime
+ # Columns, it converts each date to its Julian day number, computes the
+ # average, and then converts the average back to a DateTime.
def avg
+ return 0 if type == 'NilClass' || items.all?(&:nil?)
+
only_with('avg', 'DateTime', 'Numeric')
itms = items.filter_to_type(type)
size = itms.size.to_d
if type == 'DateTime'
avg_jd = itms.map(&:jd).sum / size
@@ -291,15 +305,18 @@
end
# :category: Aggregates
# Return the sample variance (the unbiased estimator of the population
- # variance using a divisor of N-1) as the average squared deviation from the
- # mean, of the non-nil items in the Column. Works with numeric and datetime
- # Columns. For datetime Columns, it converts each date to its Julian day
- # number and computes the variance of those numbers.
+ # variance using a divisor of N-1) as the average squared deviation from
+ # the mean, of the non-nil items in the Column, or 0 if all items are
+ # nil. Works with numeric and datetime Columns. For datetime Columns, it
+ # converts each date to its Julian day number and computes the variance of
+ # those numbers.
def var
+ return 0 if type == 'NilClass' || items.all?(&:nil?)
+
only_with('var', 'DateTime', 'Numeric')
all_items =
if type == 'DateTime'
items.filter_to_type(type).map(&:jd)
else
@@ -317,86 +334,103 @@
# :category: Aggregates
# Return the population variance (the biased estimator of the population
# variance using a divisor of N) as the average squared deviation from the
- # mean, of the non-nil items in the Column. Works with numeric and datetime
- # Columns. For datetime Columns, it converts each date to its Julian day
- # number and computes the variance of those numbers.
+ # mean, of the non-nil items in the Column, or 0 if all items are
+ # nil. Works with numeric and datetime Columns. For datetime Columns, it
+ # converts each date to its Julian day number and computes the variance of
+ # those numbers.
def pvar
+ return 0 if type == 'NilClass' || items.all?(&:nil?)
+
only_with('var', 'DateTime', 'Numeric')
n = items.filter_to_type(type).size.to_d
return BigDecimal('0.0') if n <= 1
var * ((n - 1) / n)
end
# :category: Aggregates
# Return the sample standard deviation (the unbiased estimator of the
# population standard deviation using a divisor of N-1) as the square root
- # of the sample variance, of the non-nil items in the Column. Works with
- # numeric and datetime Columns. For datetime Columns, it converts each date
- # to its Julian day number and computes the standard deviation of those
- # numbers.
+ # of the sample variance, of the non-nil items in the Column, or 0 if all
+ # items are nil. Works with numeric and datetime Columns. For datetime
+ # Columns, it converts each date to its Julian day number and computes the
+ # standard deviation of those numbers.
def dev
+ return 0 if type == 'NilClass' || items.all?(&:nil?)
+
only_with('dev', 'DateTime', 'Numeric')
var.sqrt(20)
end
# :category: Aggregates
# Return the population standard deviation (the biased estimator of the
- # population standard deviation using a divisor of N) as the square root of
- # the population variance, of the non-nil items in the Column. Works with
- # numeric and datetime Columns. For datetime Columns, it converts each date
- # to its Julian day number and computes the standard deviation of those
- # numbers.
+ # population standard deviation using a divisor of N) as the square root
+ # of the population variance, of the non-nil items in the Column, or 0 if
+ # all items are nil. Works with numeric and datetime Columns. For datetime
+ # Columns, it converts each date to its Julian day number and computes the
+ # standard deviation of those numbers.
def pdev
+ return 0 if type == 'NilClass' || items.all?(&:nil?)
+
only_with('dev', 'DateTime', 'Numeric')
Math.sqrt(pvar)
end
# :category: Aggregates
# Return true if any of the items in the Column are true; otherwise return
- # false. Works only with boolean Columns.
+ # false, or false if all items are nil. Works only with boolean Columns.
def any?
+ return false if type == 'NilClass' || items.all?(&:nil?)
+
only_with('any?', 'Boolean')
items.filter_to_type(type).any?
end
# :category: Aggregates
# Return true if all of the items in the Column are true; otherwise return
- # false. Works only with boolean Columns.
+ # false, or false if all items are nil. Works only with boolean Columns.
def all?
+ return false if type == 'NilClass' || items.all?(&:nil?)
+
only_with('all?', 'Boolean')
items.filter_to_type(type).all?
end
# :category: Aggregates
- # Return true if none of the items in the Column are true; otherwise return
- # false. Works only with boolean Columns.
+ # Return true if none of the items in the Column are true; otherwise
+ # return false, or true if all items are nil. Works only with boolean
+ # Columns.
def none?
+ return true if type == 'NilClass' || items.all?(&:nil?)
+
only_with('none?', 'Boolean')
items.filter_to_type(type).none?
end
# :category: Aggregates
# Return true if precisely one of the items in the Column is true;
# otherwise return false. Works only with boolean Columns.
def one?
+ return false if type == 'NilClass' || items.all?(&:nil?)
+
only_with('one?', 'Boolean')
items.filter_to_type(type).one?
end
private
def only_with(agg, *valid_types)
return self if valid_types.include?(type)
+
msg = "aggregate '#{agg}' cannot be applied to a #{type} column"
raise UserError, msg
end
public
@@ -434,10 +468,15 @@
end
private
def convert_and_set_type(val)
- new_val = Convert.convert_to_type(val, type, tolerant: tolerant?)
+ begin
+ new_val = Convert.convert_to_type(val, type, tolerant: tolerant?)
+ rescue IncompatibleTypeError
+ err_msg = "attempt to add '#{val}' to column '#{header}' already typed as #{type}"
+ raise IncompatibleTypeError, err_msg
+ end
if new_val && (type == 'NilClass' || type == 'String')
@type =
if [true, false].include?(new_val)
'Boolean'
elsif new_val.is_a?(Date) || new_val.is_a?(DateTime)