charty 0.1.4.dev → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +71 -0
- data/.github/workflows/nmatrix.yml +67 -0
- data/.github/workflows/pycall.yml +86 -0
- data/Dockerfile.dev +9 -1
- data/Gemfile +18 -0
- data/README.md +128 -9
- data/Rakefile +4 -5
- data/charty.gemspec +7 -2
- data/examples/Gemfile +1 -0
- data/examples/active_record.ipynb +34 -34
- data/examples/daru.ipynb +71 -29
- data/examples/iris_dataset.ipynb +12 -5
- data/examples/nmatrix.ipynb +30 -30
- data/examples/numo_narray.ipynb +245 -0
- data/examples/palette.rb +71 -0
- data/examples/sample.png +0 -0
- data/examples/sample_bokeh.ipynb +156 -0
- data/examples/sample_google_chart.ipynb +229 -68
- data/examples/sample_gruff.ipynb +148 -133
- data/examples/sample_images/bar_bokeh.html +85 -0
- data/examples/sample_images/barh_bokeh.html +85 -0
- data/examples/sample_images/barh_gruff.png +0 -0
- data/examples/sample_images/box_plot_bokeh.html +85 -0
- data/examples/sample_images/{boxplot_pyplot.png → box_plot_pyplot.png} +0 -0
- data/examples/sample_images/curve_bokeh.html +85 -0
- data/examples/sample_images/curve_with_function_bokeh.html +85 -0
- data/examples/sample_images/{errorbar_pyplot.png → error_bar_pyplot.png} +0 -0
- data/examples/sample_images/hist_gruff.png +0 -0
- data/examples/sample_images/scatter_bokeh.html +85 -0
- data/examples/sample_pyplot.ipynb +37 -35
- data/images/penguins_body_mass_g_flipper_length_mm_scatter_plot.png +0 -0
- data/images/penguins_body_mass_g_flipper_length_mm_species_scatter_plot.png +0 -0
- data/images/penguins_body_mass_g_flipper_length_mm_species_sex_scatter_plot.png +0 -0
- data/images/penguins_species_body_mass_g_bar_plot_h.png +0 -0
- data/images/penguins_species_body_mass_g_bar_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_box_plot_h.png +0 -0
- data/images/penguins_species_body_mass_g_box_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_sex_bar_plot_v.png +0 -0
- data/images/penguins_species_body_mass_g_sex_box_plot_v.png +0 -0
- data/lib/charty.rb +13 -7
- data/lib/charty/backend_methods.rb +8 -0
- data/lib/charty/backends.rb +80 -0
- data/lib/charty/backends/bokeh.rb +80 -0
- data/lib/charty/backends/google_charts.rb +267 -0
- data/lib/charty/backends/gruff.rb +104 -67
- data/lib/charty/backends/plotly.rb +549 -0
- data/lib/charty/backends/pyplot.rb +584 -86
- data/lib/charty/backends/rubyplot.rb +82 -74
- data/lib/charty/backends/unicode_plot.rb +79 -0
- data/lib/charty/index.rb +213 -0
- data/lib/charty/linspace.rb +1 -1
- data/lib/charty/missing_value_support.rb +14 -0
- data/lib/charty/plot_methods.rb +184 -0
- data/lib/charty/plotter.rb +57 -41
- data/lib/charty/plotters.rb +11 -0
- data/lib/charty/plotters/abstract_plotter.rb +156 -0
- data/lib/charty/plotters/bar_plotter.rb +216 -0
- data/lib/charty/plotters/box_plotter.rb +94 -0
- data/lib/charty/plotters/categorical_plotter.rb +380 -0
- data/lib/charty/plotters/count_plotter.rb +7 -0
- data/lib/charty/plotters/estimation_support.rb +84 -0
- data/lib/charty/plotters/random_support.rb +25 -0
- data/lib/charty/plotters/relational_plotter.rb +518 -0
- data/lib/charty/plotters/scatter_plotter.rb +115 -0
- data/lib/charty/plotters/vector_plotter.rb +6 -0
- data/lib/charty/statistics.rb +114 -0
- data/lib/charty/table.rb +82 -3
- data/lib/charty/table_adapters.rb +25 -0
- data/lib/charty/table_adapters/active_record_adapter.rb +63 -0
- data/lib/charty/table_adapters/base_adapter.rb +69 -0
- data/lib/charty/table_adapters/daru_adapter.rb +70 -0
- data/lib/charty/table_adapters/datasets_adapter.rb +49 -0
- data/lib/charty/table_adapters/hash_adapter.rb +224 -0
- data/lib/charty/table_adapters/narray_adapter.rb +76 -0
- data/lib/charty/table_adapters/nmatrix_adapter.rb +67 -0
- data/lib/charty/table_adapters/pandas_adapter.rb +81 -0
- data/lib/charty/vector.rb +69 -0
- data/lib/charty/vector_adapters.rb +183 -0
- data/lib/charty/vector_adapters/array_adapter.rb +109 -0
- data/lib/charty/vector_adapters/daru_adapter.rb +171 -0
- data/lib/charty/vector_adapters/narray_adapter.rb +187 -0
- data/lib/charty/vector_adapters/nmatrix_adapter.rb +37 -0
- data/lib/charty/vector_adapters/numpy_adapter.rb +168 -0
- data/lib/charty/vector_adapters/pandas_adapter.rb +200 -0
- data/lib/charty/version.rb +1 -1
- metadata +127 -13
- data/.travis.yml +0 -11
- data/examples/numo-narray.ipynb +0 -234
- data/lib/charty/backends/google_chart.rb +0 -167
- data/lib/charty/plotter_adapter.rb +0 -17
@@ -0,0 +1,115 @@
|
|
1
|
+
module Charty
|
2
|
+
module Plotters
|
3
|
+
class ScatterPlotter < RelationalPlotter
|
4
|
+
def initialize(data: nil, variables: {}, **options, &block)
|
5
|
+
x, y, color, style, size = variables.values_at(:x, :y, :color, :style, :size)
|
6
|
+
super(x, y, color, style, size, data: data, **options, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :alpha, :legend
|
10
|
+
|
11
|
+
def alpha=(val)
|
12
|
+
case val
|
13
|
+
when nil, :auto, 0..1
|
14
|
+
@alpha = val
|
15
|
+
when "auto"
|
16
|
+
@alpha = val.to_sym
|
17
|
+
when Numeric
|
18
|
+
raise ArgumentError,
|
19
|
+
"the given alpha is out of bounds " +
|
20
|
+
"(%p for nil, :auto, or number 0..1)" % val
|
21
|
+
else
|
22
|
+
raise ArgumentError,
|
23
|
+
"invalid value of alpha " +
|
24
|
+
"(%p for nil, :auto, or number in 0..1)" % val
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def legend=(val)
|
29
|
+
case val
|
30
|
+
when :auto, :brief, :full, false
|
31
|
+
@legend = val
|
32
|
+
when "auto", "brief", "full"
|
33
|
+
@legend = val.to_sym
|
34
|
+
else
|
35
|
+
raise ArgumentError,
|
36
|
+
"invalid value of legend (%p for :auto, :brief, :full, or false)" % val
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :line_width, :edge_color
|
41
|
+
|
42
|
+
def line_width=(val)
|
43
|
+
@line_width = check_number(val, :line_width, allow_nil: true)
|
44
|
+
end
|
45
|
+
|
46
|
+
def edge_color=(val)
|
47
|
+
@line_width = check_color(val, :edge_color, allow_nil: true)
|
48
|
+
end
|
49
|
+
|
50
|
+
def render
|
51
|
+
backend = Backends.current
|
52
|
+
backend.begin_figure
|
53
|
+
draw_points(backend)
|
54
|
+
annotate_axes(backend)
|
55
|
+
backend.show
|
56
|
+
end
|
57
|
+
|
58
|
+
def save(filename, **opts)
|
59
|
+
backend = Backends.current
|
60
|
+
backend.begin_figure
|
61
|
+
draw_points(backend)
|
62
|
+
annotate_axes(backend)
|
63
|
+
backend.save(filename, **opts)
|
64
|
+
end
|
65
|
+
|
66
|
+
private def draw_points(backend)
|
67
|
+
map_color(palette: palette, order: color_order, norm: color_norm)
|
68
|
+
map_size(sizes: sizes, order: size_order, norm: size_norm)
|
69
|
+
map_style(markers: markers, order: marker_order)
|
70
|
+
|
71
|
+
data = @plot_data.drop_na
|
72
|
+
|
73
|
+
# TODO: shold pass key_color to backend's scatter method.
|
74
|
+
# In pyplot backend, it is passed as color parameter.
|
75
|
+
|
76
|
+
x = data[:x]
|
77
|
+
y = data[:y]
|
78
|
+
color = data[:color] if @variables.key?(:color)
|
79
|
+
style = data[:style] if @variables.key?(:style)
|
80
|
+
size = data[:size] if @variables.key?(:size)
|
81
|
+
|
82
|
+
# TODO: key_color
|
83
|
+
backend.scatter(
|
84
|
+
x, y, @variables,
|
85
|
+
color: color, color_mapper: @color_mapper,
|
86
|
+
style: style, style_mapper: @style_mapper,
|
87
|
+
size: size, size_mapper: @size_mapper,
|
88
|
+
legend: legend
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
private def annotate_axes(backend)
|
93
|
+
xlabel = self.variables[:x]
|
94
|
+
ylabel = self.variables[:y]
|
95
|
+
backend.set_xlabel(xlabel) unless xlabel.nil?
|
96
|
+
backend.set_ylabel(ylabel) unless ylabel.nil?
|
97
|
+
|
98
|
+
if legend
|
99
|
+
add_legend_data(backend)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private def add_legend_data(backend)
|
104
|
+
# TODO: Legend Support
|
105
|
+
verbosity = legend
|
106
|
+
verbosity = :auto if verbosity == true
|
107
|
+
|
108
|
+
titles = [:color, :size, :style].filter_map do |v|
|
109
|
+
variables[v] if variables.key?(v)
|
110
|
+
end
|
111
|
+
legend_title = titles.length == 1 ? titles[0] : ""
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Charty
|
2
|
+
module Statistics
|
3
|
+
begin
|
4
|
+
require "enumerable/statistics"
|
5
|
+
|
6
|
+
def self.mean(enum)
|
7
|
+
enum.mean
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.stdev(enum, population: false)
|
11
|
+
enum.stdev(population: population)
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
def self.mean(enum)
|
15
|
+
xs = enum.to_a
|
16
|
+
xs.sum / xs.length.to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.stdev(enum, population: false)
|
20
|
+
xs = enum.to_a
|
21
|
+
n = xs.length
|
22
|
+
mean = xs.sum.to_f / n
|
23
|
+
ddof = population ? 0 : 1
|
24
|
+
var = xs.map {|x| (x - mean)**2 }.sum / (n - ddof)
|
25
|
+
Math.sqrt(var)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.bootstrap(vector, n_boot: 2000, func: :mean, units: nil, random: nil)
|
30
|
+
n = vector.size
|
31
|
+
random = Charty::Plotters::RandomSupport.check_random(random)
|
32
|
+
func = Charty::Plotters::EstimationSupport.check_estimator(func)
|
33
|
+
|
34
|
+
if units
|
35
|
+
return structured_bootstrap(vector, n_boot, units, func, random)
|
36
|
+
end
|
37
|
+
|
38
|
+
if defined?(Pandas::Series) || defined?(Numpy::NDArray)
|
39
|
+
boot_dist = bootstrap_optimized_for_pycall(vector, n_boot, random, func)
|
40
|
+
return boot_dist if boot_dist
|
41
|
+
end
|
42
|
+
|
43
|
+
boot_dist = Array.new(n_boot) do |i|
|
44
|
+
resampler = Array.new(n) { random.rand(n) }
|
45
|
+
|
46
|
+
w ||= vector.values_at(*resampler)
|
47
|
+
|
48
|
+
case func
|
49
|
+
when :mean
|
50
|
+
mean(w)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
boot_dist
|
55
|
+
end
|
56
|
+
|
57
|
+
private_class_method def self.bootstrap_optimized_for_pycall(vector, n_boot, random, func)
|
58
|
+
case
|
59
|
+
when vector.is_a?(Charty::Vector)
|
60
|
+
bootstrap_optimized_for_pycall(vector.data, n_boot, random, func)
|
61
|
+
|
62
|
+
when defined?(Pandas::Series) && vector.is_a?(Pandas::Series) || vector.is_a?(Numpy::NDArray)
|
63
|
+
# numpy is also available when pandas is available
|
64
|
+
n = vector.size
|
65
|
+
resampler = Numpy.empty(n, dtype: Numpy.intp)
|
66
|
+
Array.new(n_boot) do |i|
|
67
|
+
# TODO: Use Numo and MemoryView to reduce execution time
|
68
|
+
# resampler = Numo::Int64.new(n).rand(n)
|
69
|
+
# w = Numpy.take(vector, resampler)
|
70
|
+
n.times {|i| resampler[i] = random.rand(n) }
|
71
|
+
w = vector.take(resampler)
|
72
|
+
|
73
|
+
case func
|
74
|
+
when :mean
|
75
|
+
w.mean
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private_class_method def self.structured_bootstrap(vector, n_boot, units, func, random)
|
82
|
+
raise NotImplementedError,
|
83
|
+
"structured bootstrapping has not been supported yet"
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.bootstrap_ci(*vectors, which, n_boot: 2000, func: :mean, units: nil, random: nil)
|
87
|
+
boot = bootstrap(*vectors, n_boot: n_boot, func: func, units: units, random: random)
|
88
|
+
q = [50 - which / 2, 50 + which / 2]
|
89
|
+
if boot.respond_to?(:percentile)
|
90
|
+
boot.percentile(q)
|
91
|
+
else
|
92
|
+
percentile(boot, q)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# TODO: optimize with introselect algorithm
|
97
|
+
def self.percentile(a, q)
|
98
|
+
return mean(a) if a.size == 0
|
99
|
+
|
100
|
+
a = a.sort
|
101
|
+
n = a.size
|
102
|
+
q.map do |x|
|
103
|
+
x = n * (x / 100.0)
|
104
|
+
i = x.floor
|
105
|
+
if i == n-1
|
106
|
+
a[i]
|
107
|
+
else
|
108
|
+
t = x - i
|
109
|
+
(1-t)*a[i] + t*a[i+1]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/charty/table.rb
CHANGED
@@ -1,11 +1,69 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
|
2
3
|
module Charty
|
4
|
+
class ColumnAccessor
|
5
|
+
def initialize(adapter)
|
6
|
+
@adapter = adapter
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](column_name)
|
10
|
+
@adapter[nil, column_name]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
3
14
|
class Table
|
4
|
-
|
5
|
-
|
15
|
+
extend Forwardable
|
16
|
+
include MissingValueSupport
|
17
|
+
|
18
|
+
def initialize(data, **kwargs)
|
19
|
+
adapter_class = TableAdapters.find_adapter_class(data)
|
20
|
+
if kwargs.empty?
|
21
|
+
@adapter = adapter_class.new(data)
|
22
|
+
else
|
23
|
+
@adapter = adapter_class.new(data, **kwargs)
|
24
|
+
end
|
25
|
+
|
26
|
+
@column_cache = {}
|
6
27
|
end
|
7
28
|
|
8
|
-
attr_reader :
|
29
|
+
attr_reader :adapter
|
30
|
+
|
31
|
+
def_delegators :adapter, :length, :column_length
|
32
|
+
|
33
|
+
def_delegators :adapter, :columns, :columns=
|
34
|
+
def_delegators :adapter, :index, :index=
|
35
|
+
|
36
|
+
def_delegator :@adapter, :column_names
|
37
|
+
def_delegator :@adapter, :data, :raw_data
|
38
|
+
|
39
|
+
def ==(other)
|
40
|
+
return true if equal?(other)
|
41
|
+
|
42
|
+
case other
|
43
|
+
when Charty::Table
|
44
|
+
adapter == other.adapter
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def empty?
|
51
|
+
length == 0
|
52
|
+
end
|
53
|
+
|
54
|
+
def [](key)
|
55
|
+
key = case key
|
56
|
+
when Symbol
|
57
|
+
key
|
58
|
+
else
|
59
|
+
String.try_convert(key).to_sym
|
60
|
+
end
|
61
|
+
if @column_cache.key?(key)
|
62
|
+
@column_cache[key]
|
63
|
+
else
|
64
|
+
@column_cache[key] = @adapter[nil, key]
|
65
|
+
end
|
66
|
+
end
|
9
67
|
|
10
68
|
def to_a(x=nil, y=nil, z=nil)
|
11
69
|
case
|
@@ -23,6 +81,8 @@ module Charty
|
|
23
81
|
else
|
24
82
|
raise ArgumentError, "column_names is required to convert to_a from ActiveRecord::Relation"
|
25
83
|
end
|
84
|
+
when table.kind_of?(Array)
|
85
|
+
table
|
26
86
|
else
|
27
87
|
raise ArgumentError, "unsupported object: #{table.inspect}"
|
28
88
|
end
|
@@ -37,5 +97,24 @@ module Charty
|
|
37
97
|
i += 1
|
38
98
|
end
|
39
99
|
end
|
100
|
+
|
101
|
+
def drop_na
|
102
|
+
# TODO: Must implement this method in each adapter
|
103
|
+
missing_index = index.select do |i|
|
104
|
+
column_names.any? do |key|
|
105
|
+
missing_value?(self[key][i])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
if missing_index.empty?
|
109
|
+
self
|
110
|
+
else
|
111
|
+
select_index = index.to_a - missing_index
|
112
|
+
new_data = column_names.map { |key|
|
113
|
+
vals = select_index.map {|i| self[key][i] }
|
114
|
+
[key, vals]
|
115
|
+
}.to_h
|
116
|
+
Charty::Table.new(new_data, index: select_index)
|
117
|
+
end
|
118
|
+
end
|
40
119
|
end
|
41
120
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Charty
|
2
|
+
module TableAdapters
|
3
|
+
@adapters = {}
|
4
|
+
|
5
|
+
def self.register(name, adapter_class)
|
6
|
+
@adapters[name] = adapter_class
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.find_adapter_class(data)
|
10
|
+
@adapters.each_value do |adapter_class|
|
11
|
+
return adapter_class if adapter_class.supported?(data)
|
12
|
+
end
|
13
|
+
raise ArgumentError, "Unsupported data class: #{data.class}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require_relative 'table_adapters/base_adapter'
|
19
|
+
require_relative 'table_adapters/hash_adapter'
|
20
|
+
require_relative 'table_adapters/narray_adapter'
|
21
|
+
require_relative 'table_adapters/datasets_adapter'
|
22
|
+
require_relative 'table_adapters/daru_adapter'
|
23
|
+
require_relative 'table_adapters/active_record_adapter'
|
24
|
+
require_relative 'table_adapters/nmatrix_adapter'
|
25
|
+
require_relative 'table_adapters/pandas_adapter'
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Charty
|
2
|
+
module TableAdapters
|
3
|
+
class ActiveRecordAdapter < BaseAdapter
|
4
|
+
TableAdapters.register(:active_record, self)
|
5
|
+
|
6
|
+
def self.supported?(data)
|
7
|
+
defined?(ActiveRecord::Relation) && data.is_a?(ActiveRecord::Relation)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(data)
|
11
|
+
@data = check_type(ActiveRecord::Relation, data, :data)
|
12
|
+
@column_names = @data.column_names.freeze
|
13
|
+
self.columns = Index.new(@column_names)
|
14
|
+
self.index = RangeIndex.new(0 ... length)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :data, :column_names
|
18
|
+
|
19
|
+
def_delegators :data, :size
|
20
|
+
|
21
|
+
alias length size
|
22
|
+
|
23
|
+
def column_length
|
24
|
+
column_names.length
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](row, column)
|
28
|
+
fetch_records unless @columns_cache
|
29
|
+
if row
|
30
|
+
@columns_cache[resolve_column_index(column)][row]
|
31
|
+
else
|
32
|
+
column_data = @columns_cache[resolve_column_index(column)]
|
33
|
+
Vector.new(column_data, index: index, name: column)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private def resolve_column_index(column)
|
38
|
+
case column
|
39
|
+
when String, Symbol
|
40
|
+
index = column_names.index(column.to_s)
|
41
|
+
unless index
|
42
|
+
raise IndexError, "invalid column name: #{column.inspect}"
|
43
|
+
end
|
44
|
+
index
|
45
|
+
when Integer
|
46
|
+
column
|
47
|
+
else
|
48
|
+
message = "column must be String or Integer: #{column.inspect}"
|
49
|
+
raise ArgumentError, message
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private def fetch_records
|
54
|
+
@columns_cache = @data.pluck(*column_names).transpose
|
55
|
+
end
|
56
|
+
|
57
|
+
private def check_type(type, data, name)
|
58
|
+
return data if data.is_a?(type)
|
59
|
+
raise TypeError, "#{name} must be a #{type}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Charty
|
4
|
+
module TableAdapters
|
5
|
+
class BaseAdapter
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :columns
|
10
|
+
|
11
|
+
def columns=(values)
|
12
|
+
@columns = check_and_convert_index(values, :columns, column_length)
|
13
|
+
end
|
14
|
+
|
15
|
+
def column_names
|
16
|
+
columns.to_a
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :index
|
20
|
+
|
21
|
+
def index=(values)
|
22
|
+
@index = check_and_convert_index(values, :index, length)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
case other
|
27
|
+
when BaseAdapter
|
28
|
+
return false if columns != other.columns
|
29
|
+
return false if index != other.index
|
30
|
+
compare_data_equality(other)
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def compare_data_equality(other)
|
37
|
+
columns.each do |name|
|
38
|
+
if self[nil, name] != other[nil, name]
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
private def check_and_convert_index(values, name, expected_length)
|
46
|
+
case values
|
47
|
+
when Index, Range
|
48
|
+
else
|
49
|
+
unless (ary = Array.try_convert(values))
|
50
|
+
raise ArgumentError, "invalid object for %s: %p" % [name, values]
|
51
|
+
end
|
52
|
+
values = ary
|
53
|
+
end
|
54
|
+
if expected_length != values.size
|
55
|
+
raise ArgumentError,
|
56
|
+
"invalid length for %s (%d for %d)" % [name, values.size, expected_length]
|
57
|
+
end
|
58
|
+
case values
|
59
|
+
when Index
|
60
|
+
values
|
61
|
+
when Range
|
62
|
+
RangeIndex.new(values)
|
63
|
+
when Array
|
64
|
+
Index.new(values)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|