charty 0.2.0 → 0.2.6

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +71 -0
  3. data/.github/workflows/nmatrix.yml +67 -0
  4. data/.github/workflows/pycall.yml +86 -0
  5. data/Dockerfile.dev +9 -1
  6. data/Gemfile +18 -0
  7. data/README.md +177 -9
  8. data/Rakefile +4 -5
  9. data/charty.gemspec +10 -4
  10. data/examples/Gemfile +1 -0
  11. data/examples/active_record.ipynb +1 -1
  12. data/examples/daru.ipynb +1 -1
  13. data/examples/iris_dataset.ipynb +1 -1
  14. data/examples/nmatrix.ipynb +1 -1
  15. data/examples/{numo-narray.ipynb → numo_narray.ipynb} +1 -1
  16. data/examples/palette.rb +71 -0
  17. data/examples/sample.png +0 -0
  18. data/examples/sample_images/hist_gruff.png +0 -0
  19. data/examples/sample_pyplot.ipynb +40 -38
  20. data/images/penguins_body_mass_g_flipper_length_mm_scatter_plot.png +0 -0
  21. data/images/penguins_body_mass_g_flipper_length_mm_species_scatter_plot.png +0 -0
  22. data/images/penguins_body_mass_g_flipper_length_mm_species_sex_scatter_plot.png +0 -0
  23. data/images/penguins_species_body_mass_g_bar_plot_h.png +0 -0
  24. data/images/penguins_species_body_mass_g_bar_plot_v.png +0 -0
  25. data/images/penguins_species_body_mass_g_box_plot_h.png +0 -0
  26. data/images/penguins_species_body_mass_g_box_plot_v.png +0 -0
  27. data/images/penguins_species_body_mass_g_sex_bar_plot_v.png +0 -0
  28. data/images/penguins_species_body_mass_g_sex_box_plot_v.png +0 -0
  29. data/lib/charty.rb +13 -1
  30. data/lib/charty/backend_methods.rb +8 -0
  31. data/lib/charty/backends.rb +26 -1
  32. data/lib/charty/backends/bokeh.rb +31 -31
  33. data/lib/charty/backends/{google_chart.rb → google_charts.rb} +75 -33
  34. data/lib/charty/backends/gruff.rb +14 -3
  35. data/lib/charty/backends/plotly.rb +774 -9
  36. data/lib/charty/backends/pyplot.rb +611 -34
  37. data/lib/charty/backends/rubyplot.rb +2 -2
  38. data/lib/charty/backends/unicode_plot.rb +79 -0
  39. data/lib/charty/dash_pattern_generator.rb +57 -0
  40. data/lib/charty/index.rb +213 -0
  41. data/lib/charty/linspace.rb +1 -1
  42. data/lib/charty/plot_methods.rb +254 -0
  43. data/lib/charty/plotter.rb +10 -10
  44. data/lib/charty/plotters.rb +12 -0
  45. data/lib/charty/plotters/abstract_plotter.rb +243 -0
  46. data/lib/charty/plotters/bar_plotter.rb +201 -0
  47. data/lib/charty/plotters/box_plotter.rb +79 -0
  48. data/lib/charty/plotters/categorical_plotter.rb +380 -0
  49. data/lib/charty/plotters/count_plotter.rb +7 -0
  50. data/lib/charty/plotters/estimation_support.rb +84 -0
  51. data/lib/charty/plotters/line_plotter.rb +300 -0
  52. data/lib/charty/plotters/random_support.rb +25 -0
  53. data/lib/charty/plotters/relational_plotter.rb +635 -0
  54. data/lib/charty/plotters/scatter_plotter.rb +80 -0
  55. data/lib/charty/plotters/vector_plotter.rb +6 -0
  56. data/lib/charty/statistics.rb +114 -0
  57. data/lib/charty/table.rb +161 -15
  58. data/lib/charty/table_adapters.rb +2 -0
  59. data/lib/charty/table_adapters/active_record_adapter.rb +17 -9
  60. data/lib/charty/table_adapters/base_adapter.rb +166 -0
  61. data/lib/charty/table_adapters/daru_adapter.rb +41 -3
  62. data/lib/charty/table_adapters/datasets_adapter.rb +17 -2
  63. data/lib/charty/table_adapters/hash_adapter.rb +143 -16
  64. data/lib/charty/table_adapters/narray_adapter.rb +25 -6
  65. data/lib/charty/table_adapters/nmatrix_adapter.rb +15 -5
  66. data/lib/charty/table_adapters/pandas_adapter.rb +163 -0
  67. data/lib/charty/util.rb +28 -0
  68. data/lib/charty/vector.rb +69 -0
  69. data/lib/charty/vector_adapters.rb +187 -0
  70. data/lib/charty/vector_adapters/array_adapter.rb +101 -0
  71. data/lib/charty/vector_adapters/daru_adapter.rb +163 -0
  72. data/lib/charty/vector_adapters/narray_adapter.rb +182 -0
  73. data/lib/charty/vector_adapters/nmatrix_adapter.rb +37 -0
  74. data/lib/charty/vector_adapters/numpy_adapter.rb +168 -0
  75. data/lib/charty/vector_adapters/pandas_adapter.rb +199 -0
  76. data/lib/charty/version.rb +1 -1
  77. metadata +121 -22
  78. data/.travis.yml +0 -10
@@ -0,0 +1,101 @@
1
+ require "date"
2
+
3
+ module Charty
4
+ module VectorAdapters
5
+ class ArrayAdapter < BaseAdapter
6
+ VectorAdapters.register(:array, self)
7
+
8
+ extend Forwardable
9
+ include Enumerable
10
+
11
+ def self.supported?(data)
12
+ case data
13
+ when Array
14
+ case data[0]
15
+ when Numeric, String, Time, Date, DateTime, true, false, nil
16
+ true
17
+ else
18
+ false
19
+ end
20
+ else
21
+ false
22
+ end
23
+ end
24
+
25
+ def initialize(data, index: nil)
26
+ @data = check_data(data)
27
+ self.index = index || RangeIndex.new(0 ... length)
28
+ end
29
+
30
+ include NameSupport
31
+ include IndexSupport
32
+
33
+ def_delegators :data, :values_at
34
+
35
+ def where(mask)
36
+ masked_data, masked_index = where_in_array(mask)
37
+ Charty::Vector.new(masked_data, index: masked_index, name: name)
38
+ end
39
+
40
+ def first_nonnil
41
+ data.drop_while(&:nil?).first
42
+ end
43
+
44
+ def boolean?
45
+ case first_nonnil
46
+ when true, false
47
+ true
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ def numeric?
54
+ case first_nonnil
55
+ when Numeric
56
+ true
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ def categorical?
63
+ false
64
+ end
65
+
66
+ def categories
67
+ nil
68
+ end
69
+
70
+ def_delegator :data, :uniq, :unique_values
71
+
72
+ def group_by(grouper)
73
+ groups = data.each_index.group_by {|i| grouper[i] }
74
+ groups.map { |g, vals|
75
+ vals.collect! {|i| self[i] }
76
+ [g, Charty::Vector.new(vals)]
77
+ }.to_h
78
+ end
79
+
80
+ def drop_na
81
+ if numeric?
82
+ Charty::Vector.new(data.reject { |x| Util.missing?(x) })
83
+ else
84
+ Charty::Vector.new(data.compact)
85
+ end
86
+ end
87
+
88
+ def eq(val)
89
+ Charty::Vector.new(data.map {|x| x == val },
90
+ index: index,
91
+ name: name)
92
+ end
93
+
94
+ def notnull
95
+ Charty::Vector.new(data.map {|x| ! Util.missing?(x) },
96
+ index: index,
97
+ name: name)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,163 @@
1
+ module Charty
2
+ module VectorAdapters
3
+ class DaruVectorAdapter < BaseAdapter
4
+ VectorAdapters.register(:daru_vector, self)
5
+
6
+ def self.supported?(data)
7
+ defined?(Daru::Vector) && data.is_a?(Daru::Vector)
8
+ end
9
+
10
+ def initialize(data)
11
+ @data = check_data(data)
12
+ end
13
+
14
+ def_delegator :data, :size, :length
15
+
16
+ def index
17
+ DaruIndex.new(data.index)
18
+ end
19
+
20
+ def index=(new_index)
21
+ case new_index
22
+ when DaruIndex
23
+ data.index = new_index.values
24
+ when Index
25
+ data.index = new_index.to_a
26
+ else
27
+ data.index = new_index
28
+ end
29
+ end
30
+
31
+ def_delegators :data, :name, :name=
32
+
33
+ def compare_data_equality(other)
34
+ case other
35
+ when DaruVectorAdapter
36
+ data == other.data
37
+ when ArrayAdapter
38
+ data.to_a == other.data
39
+ when NArrayAdapter, NMatrixAdapter, NumpyAdapter, PandasSeriesAdapter
40
+ other.compare_data_equality(self)
41
+ else
42
+ data == other.data.to_a
43
+ end
44
+ end
45
+
46
+ def [](key)
47
+ case key
48
+ when Charty::Vector
49
+ where(key)
50
+ else
51
+ data[key]
52
+ end
53
+ end
54
+
55
+ def_delegators :data, :[]=, :to_a
56
+
57
+ def values_at(*indices)
58
+ indices.map {|i| data.at(i) }
59
+ end
60
+
61
+ def where(mask)
62
+ masked_data, masked_index = where_in_array(mask)
63
+ Charty::Vector.new(Daru::Vector.new(masked_data, index: masked_index), name: name)
64
+ end
65
+
66
+ def where_in_array(mask)
67
+ mask = check_mask_vector(mask)
68
+ masked_data = []
69
+ masked_index = []
70
+ mask.each_with_index do |f, i|
71
+ case f
72
+ when true, 1
73
+ masked_data << data[i]
74
+ masked_index << data.index.key(i)
75
+ end
76
+ end
77
+ return masked_data, masked_index
78
+ end
79
+
80
+ def first_nonnil
81
+ data.drop_while(&:nil?).first
82
+ end
83
+
84
+ def boolean?
85
+ case
86
+ when numeric?, categorical?
87
+ false
88
+ else
89
+ case first_nonnil
90
+ when true, false
91
+ true
92
+ else
93
+ false
94
+ end
95
+ end
96
+ end
97
+
98
+ def_delegators :data, :numeric?
99
+ def_delegator :data, :category?, :categorical?
100
+
101
+ def categories
102
+ data.categories.compact if categorical?
103
+ end
104
+
105
+ def unique_values
106
+ data.uniq.to_a
107
+ end
108
+
109
+ def group_by(grouper)
110
+ case grouper
111
+ when Daru::Vector
112
+ if grouper.category?
113
+ # TODO: A categorical Daru::Vector cannot perform group_by well
114
+ grouper = Daru::Vector.new(grouper.to_a)
115
+ end
116
+ groups = grouper.group_by.groups
117
+ groups.map { |g, indices|
118
+ [g.first, Charty::Vector.new(data[*indices])]
119
+ }.to_h
120
+ when Charty::Vector
121
+ case grouper.data
122
+ when Daru::Vector
123
+ return group_by(grouper.data)
124
+ else
125
+ return group_by(Daru::Vector.new(grouper.to_a))
126
+ end
127
+ else
128
+ return group_by(Charty::Vector.new(grouper))
129
+ end
130
+ end
131
+
132
+ def drop_na
133
+ values = data.reject {|x| Util.missing?(x) }
134
+ Charty::Vector.new(Daru::Vector.new(values))
135
+ end
136
+
137
+ def eq(val)
138
+ Charty::Vector.new(data.eq(val).to_a,
139
+ index: data.index.to_a,
140
+ name: name)
141
+ end
142
+
143
+ def notnull
144
+ notnull_data = data.map {|x| ! Util.missing?(x) }
145
+ Charty::Vector.new(notnull_data, index: data.index.to_a, name: name)
146
+ end
147
+
148
+ def_delegator :data, :mean
149
+
150
+ def stdev(population: false)
151
+ if population
152
+ data.standard_deviation_sample
153
+ else
154
+ data.standard_deviation_population
155
+ end
156
+ end
157
+
158
+ def percentile(q)
159
+ data.linear_percentile(q)
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,182 @@
1
+ module Charty
2
+ module VectorAdapters
3
+ class NArrayAdapter < BaseAdapter
4
+ VectorAdapters.register(:narray, self)
5
+
6
+ extend Forwardable
7
+ include Enumerable
8
+
9
+ def self.supported?(data)
10
+ defined?(Numo::NArray) && data.is_a?(Numo::NArray)
11
+ end
12
+
13
+ def initialize(data)
14
+ @data = check_data(data)
15
+ self.index = index || RangeIndex.new(0 ... length)
16
+ end
17
+
18
+ def compare_data_equality(other)
19
+ case other
20
+ when ArrayAdapter, NArrayAdapter
21
+ data == other.data
22
+ when NumpyAdapter, PandasSeriesAdapter
23
+ other.compare_data_equality(self)
24
+ else
25
+ data == other.data.to_a
26
+ end
27
+ end
28
+
29
+ include NameSupport
30
+ include IndexSupport
31
+
32
+ def where(mask)
33
+ mask = check_mask_vector(mask)
34
+ case mask.data
35
+ when Numo::Bit
36
+ bits = mask.data
37
+ masked_data = data[bits]
38
+ masked_index = bits.where.map {|i| index[i] }.to_a
39
+ else
40
+ masked_data, masked_index = where_in_array(mask)
41
+ masked_data = data.class[*masked_data]
42
+ end
43
+ Charty::Vector.new(masked_data, index: masked_index, name: name)
44
+ end
45
+
46
+ def boolean?
47
+ case data
48
+ when Numo::Bit
49
+ true
50
+ when Numo::RObject
51
+ i, n = 0, data.size
52
+ while i < n
53
+ case data[i]
54
+ when nil, true, false
55
+ # do nothing
56
+ else
57
+ return false
58
+ end
59
+ i += 1
60
+ end
61
+ true
62
+ else
63
+ false
64
+ end
65
+ end
66
+
67
+ def numeric?
68
+ case data
69
+ when Numo::Bit,
70
+ Numo::RObject
71
+ false
72
+ else
73
+ true
74
+ end
75
+ end
76
+
77
+ def categorical?
78
+ false
79
+ end
80
+
81
+ def categories
82
+ nil
83
+ end
84
+
85
+ def unique_values
86
+ existence = {}
87
+ i, n = 0, data.size
88
+ unique = []
89
+ while i < n
90
+ x = data[i]
91
+ unless existence[x]
92
+ unique << x
93
+ existence[x] = true
94
+ end
95
+ i += 1
96
+ end
97
+ unique
98
+ end
99
+
100
+ def group_by(grouper)
101
+ case grouper
102
+ when Charty::Vector
103
+ # nothing to do
104
+ else
105
+ grouper = Charty::Vector.new(grouper)
106
+ end
107
+
108
+ group_keys = grouper.unique_values
109
+
110
+ case grouper.data
111
+ when Numo::NArray
112
+ grouper = grouper.data
113
+ else
114
+ grouper = Numo::NArray[*grouper.to_a]
115
+ end
116
+
117
+ group_keys.map { |g|
118
+ [g, Charty::Vector.new(data[grouper.eq(g)])]
119
+ }.to_h
120
+ end
121
+
122
+ def drop_na
123
+ case data
124
+ when Numo::DFloat, Numo::SFloat, Numo::DComplex, Numo::SComplex
125
+ Charty::Vector.new(data[~data.isnan])
126
+ when Numo::RObject
127
+ where_is_nan = data.isnan
128
+ values = []
129
+ i, n = 0, data.size
130
+ while i < n
131
+ x = data[i]
132
+ unless x.nil? || where_is_nan[i] == 1
133
+ values << x
134
+ end
135
+ i += 1
136
+ end
137
+ Charty::Vector.new(Numo::RObject[*values])
138
+ else
139
+ self
140
+ end
141
+ end
142
+
143
+ def eq(val)
144
+ Charty::Vector.new(data.eq(val),
145
+ index: index,
146
+ name: name)
147
+ end
148
+
149
+ def notnull
150
+ case data
151
+ when Numo::RObject
152
+ i, n = 0, length
153
+ notnull_data = Numo::Bit.zeros(n)
154
+ while i < n
155
+ notnull_data[i] = ! Util.missing?(data[i])
156
+ i += 1
157
+ end
158
+ when ->(x) { x.respond_to?(:isnan) }
159
+ notnull_data = ~data.isnan
160
+ else
161
+ notnull_data = Numo::Bit.ones(length)
162
+ end
163
+ Charty::Vector.new(notnull_data, index: index, name: name)
164
+ end
165
+
166
+ def mean
167
+ data.mean(nan: true)
168
+ end
169
+
170
+ def stdev(population: false)
171
+ s = data.stddev(nan: true)
172
+ if population
173
+ # Numo::NArray does not support population standard deviation
174
+ n = data.isnan.sum
175
+ s * (n - 1) / n
176
+ else
177
+ s
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,37 @@
1
+ module Charty
2
+ module VectorAdapters
3
+ class NMatrixAdapter < BaseAdapter
4
+ VectorAdapters.register(:nmatrix, self)
5
+
6
+ extend Forwardable
7
+ include Enumerable
8
+
9
+ def self.supported?(data)
10
+ defined?(NMatrix) && data.is_a?(NMatrix)
11
+ end
12
+
13
+ def initialize(data)
14
+ @data = check_data(data)
15
+ self.index = index || RangeIndex.new(0 ... length)
16
+ end
17
+
18
+ def compare_data_equality(other)
19
+ case other
20
+ when NMatrixAdapter
21
+ data == other.data
22
+ when ArrayAdapter, DaruVectorAdapter
23
+ data.to_a == other.data.to_a
24
+ when NArrayAdapter, NumpyAdapter, PandasSeriesAdapter
25
+ other.compare_data_equality(self)
26
+ else
27
+ data == other.data.to_a
28
+ end
29
+ end
30
+
31
+ include NameSupport
32
+ include IndexSupport
33
+
34
+ alias length size
35
+ end
36
+ end
37
+ end