charty 0.2.7 → 0.2.11

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/charty.gemspec +1 -0
  3. data/examples/bar_plot.rb +19 -0
  4. data/examples/box_plot.rb +17 -0
  5. data/examples/scatter_plot.rb +17 -0
  6. data/images/penguins_body_mass_g_flipper_length_mm_species_scatter_plot.png +0 -0
  7. data/images/penguins_body_mass_g_flipper_length_mm_species_sex_scatter_plot.png +0 -0
  8. data/images/penguins_species_body_mass_g_bar_plot_h.png +0 -0
  9. data/images/penguins_species_body_mass_g_bar_plot_v.png +0 -0
  10. data/images/penguins_species_body_mass_g_box_plot_h.png +0 -0
  11. data/images/penguins_species_body_mass_g_box_plot_v.png +0 -0
  12. data/images/penguins_species_body_mass_g_sex_bar_plot_v.png +0 -0
  13. data/images/penguins_species_body_mass_g_sex_box_plot_v.png +0 -0
  14. data/lib/charty/backends/plotly.rb +53 -22
  15. data/lib/charty/backends/plotly_helpers/notebook_renderer.rb +4 -1
  16. data/lib/charty/backends/pyplot.rb +73 -0
  17. data/lib/charty/backends/unicode_plot.rb +16 -11
  18. data/lib/charty/index.rb +9 -0
  19. data/lib/charty/plot_methods.rb +46 -10
  20. data/lib/charty/plotters/abstract_plotter.rb +41 -9
  21. data/lib/charty/plotters/bar_plotter.rb +39 -0
  22. data/lib/charty/plotters/categorical_plotter.rb +9 -1
  23. data/lib/charty/plotters/distribution_plotter.rb +44 -7
  24. data/lib/charty/plotters/histogram_plotter.rb +97 -35
  25. data/lib/charty/plotters/line_plotter.rb +38 -5
  26. data/lib/charty/plotters/scatter_plotter.rb +4 -2
  27. data/lib/charty/statistics.rb +2 -2
  28. data/lib/charty/table.rb +30 -23
  29. data/lib/charty/table_adapters/arrow_adapter.rb +53 -0
  30. data/lib/charty/table_adapters/base_adapter.rb +88 -0
  31. data/lib/charty/table_adapters/daru_adapter.rb +41 -1
  32. data/lib/charty/table_adapters/hash_adapter.rb +58 -10
  33. data/lib/charty/table_adapters/pandas_adapter.rb +49 -1
  34. data/lib/charty/table_adapters.rb +1 -0
  35. data/lib/charty/vector.rb +30 -2
  36. data/lib/charty/vector_adapters/array_adapter.rb +1 -1
  37. data/lib/charty/vector_adapters/arrow_adapter.rb +156 -0
  38. data/lib/charty/vector_adapters/daru_adapter.rb +3 -6
  39. data/lib/charty/vector_adapters/narray_adapter.rb +10 -1
  40. data/lib/charty/vector_adapters/nmatrix_adapter.rb +1 -5
  41. data/lib/charty/vector_adapters/numpy_adapter.rb +4 -0
  42. data/lib/charty/vector_adapters/pandas_adapter.rb +10 -1
  43. data/lib/charty/vector_adapters/vector_adapter.rb +62 -0
  44. data/lib/charty/vector_adapters.rb +22 -0
  45. data/lib/charty/version.rb +1 -1
  46. metadata +23 -3
@@ -70,8 +70,10 @@ module Charty
70
70
  end
71
71
 
72
72
  private def annotate_axes(backend)
73
- xlabel = self.variables[:x]
74
- ylabel = self.variables[:y]
73
+ backend.set_title(self.title) if self.title
74
+
75
+ xlabel = self.x_label || self.variables[:x]
76
+ ylabel = self.y_label || self.variables[:y]
75
77
  backend.set_xlabel(xlabel) unless xlabel.nil?
76
78
  backend.set_ylabel(ylabel) unless ylabel.nil?
77
79
  end
@@ -109,9 +109,9 @@ module Charty
109
109
  a = a.sort
110
110
  n = a.size
111
111
  q.map do |x|
112
- x = n * (x / 100.0)
112
+ x = (n-1) * (x / 100.0)
113
113
  i = x.floor
114
- if i == n-1
114
+ if i == x
115
115
  a[i]
116
116
  else
117
117
  t = x - i
data/lib/charty/table.rb CHANGED
@@ -32,20 +32,7 @@ module Charty
32
32
  def_delegators :adapter, :columns, :columns=
33
33
  def_delegators :adapter, :index, :index=
34
34
 
35
- def_delegator :@adapter, :column_names
36
-
37
- def column?(name)
38
- return true if column_names.include?(name)
39
-
40
- case name
41
- when String
42
- column_names.include?(name.to_sym)
43
- when Symbol
44
- column_names.include?(name.to_s)
45
- else
46
- false
47
- end
48
- end
35
+ def_delegators :@adapter, :column_names, :column?
49
36
 
50
37
  def_delegator :@adapter, :data, :raw_data
51
38
 
@@ -65,17 +52,35 @@ module Charty
65
52
  end
66
53
 
67
54
  def [](key)
68
- key = case key
69
- when Symbol
70
- key
71
- else
72
- String.try_convert(key).to_sym
73
- end
74
- if @column_cache.key?(key)
75
- @column_cache[key]
55
+ case key
56
+ when Array
57
+ @adapter[nil, key]
58
+ else
59
+ key = case key
60
+ when Symbol
61
+ key
62
+ else
63
+ String.try_convert(key).to_sym
64
+ end
65
+ if @column_cache.key?(key)
66
+ @column_cache[key]
67
+ else
68
+ @column_cache[key] = @adapter[nil, key]
69
+ end
70
+ end
71
+ end
72
+
73
+ def []=(key, values)
74
+ case key
75
+ when Array
76
+ raise ArgumentError,
77
+ "Substituting multiple keys is not supported"
78
+ when Symbol
79
+ # do nothing
76
80
  else
77
- @column_cache[key] = @adapter[nil, key]
81
+ key = key.to_str.to_sym
78
82
  end
83
+ @adapter[key] = values
79
84
  end
80
85
 
81
86
  def group_by(grouper, sort: true, drop_na: true)
@@ -123,6 +128,8 @@ module Charty
123
128
 
124
129
  def_delegator :adapter, :reset_index
125
130
 
131
+ def_delegator :adapter, :melt
132
+
126
133
  class GroupByBase
127
134
  end
128
135
 
@@ -0,0 +1,53 @@
1
+ module Charty
2
+ module TableAdapters
3
+ class ArrowAdapter < BaseAdapter
4
+ TableAdapters.register(:arrow, self)
5
+
6
+ def self.supported?(data)
7
+ defined?(Arrow::Table) && data.is_a?(Arrow::Table)
8
+ end
9
+
10
+ def initialize(data)
11
+ @data = data
12
+ @column_names = @data.columns.map(&:name)
13
+ self.columns = Index.new(@column_names)
14
+ self.index = RangeIndex.new(0 ... length)
15
+ end
16
+
17
+ attr_reader :data
18
+
19
+ def length
20
+ @data.n_rows
21
+ end
22
+
23
+ def column_length
24
+ @column_names.length
25
+ end
26
+
27
+ def compare_data_equality(other)
28
+ case other
29
+ when ArrowAdapter
30
+ data == other.data
31
+ else
32
+ super
33
+ end
34
+ end
35
+
36
+ def [](row, column)
37
+ if row
38
+ @data[column][row]
39
+ else
40
+ case column
41
+ when Array
42
+ Table.new(@data.select_columns(*column))
43
+ else
44
+ column_data = @data[column]
45
+ Vector.new(column_data.data.combine,
46
+ index: index,
47
+ name: column_data.name)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -16,6 +16,19 @@ module Charty
16
16
  columns.to_a
17
17
  end
18
18
 
19
+ def column?(name)
20
+ return true if column_names.include?(name)
21
+
22
+ case name
23
+ when String
24
+ column_names.include?(name.to_sym)
25
+ when Symbol
26
+ column_names.include?(name.to_s)
27
+ else
28
+ false
29
+ end
30
+ end
31
+
19
32
  attr_reader :index
20
33
 
21
34
  def index=(values)
@@ -126,6 +139,81 @@ module Charty
126
139
  )
127
140
  end
128
141
 
142
+ def melt(id_vars: nil, value_vars: nil, var_name: nil, value_name: :value)
143
+ if column?(value_name)
144
+ raise ArgumentError,
145
+ "The value of `value_name` must not be matched to the existing column names."
146
+ end
147
+
148
+ case value_name
149
+ when Symbol
150
+ # do nothing
151
+ else
152
+ value_name = value.to_str.to_sym
153
+ end
154
+
155
+ id_vars = check_melt_vars(id_vars, :id_vars)
156
+ value_vars = check_melt_vars(value_vars, :value_vars) { self.column_names }
157
+ value_vars -= id_vars
158
+
159
+ case var_name
160
+ when nil
161
+ var_name = self.columns.name
162
+ var_name = :variable if var_name.nil?
163
+ when Symbol
164
+ # do nothing
165
+ else
166
+ var_name = var_name.to_str
167
+ end
168
+ var_name = var_name.to_sym
169
+
170
+ n_batch_rows = self.length
171
+ n_target_columns = value_vars.length
172
+ melted_data = id_vars.map { |cn|
173
+ id_values = self[nil, cn].to_a
174
+ [cn.to_sym, id_values * n_target_columns]
175
+ }.to_h
176
+
177
+ melted_data[var_name] = value_vars.map { |cn| Array.new(n_batch_rows, cn) }.flatten
178
+
179
+ melted_data[value_name] = value_vars.map { |cn| self[nil, cn].to_a }.flatten
180
+
181
+ Charty::Table.new(melted_data)
182
+ end
183
+
184
+ private def check_melt_vars(val, name)
185
+ if val.nil?
186
+ val = if block_given?
187
+ yield
188
+ else
189
+ []
190
+ end
191
+ end
192
+ case val
193
+ when nil
194
+ nil
195
+ when Array
196
+ missing = val.reject {|cn| self.column?(cn) }
197
+ if missing.empty?
198
+ val.map do |v|
199
+ case v
200
+ when Symbol
201
+ v.to_s
202
+ else
203
+ v.to_str
204
+ end
205
+ end
206
+ else
207
+ raise ArgumentError,
208
+ "Missing column names in `#{name}` (%s)" % missing.join(", ")
209
+ end
210
+ when Symbol
211
+ [val.to_s]
212
+ else
213
+ [val.to_str]
214
+ end
215
+ end
216
+
129
217
  private def check_na_position(val)
130
218
  case val
131
219
  when :first, "first"
@@ -57,12 +57,52 @@ module Charty
57
57
  column_data = if @data.has_vector?(column)
58
58
  @data[column]
59
59
  else
60
- @data[column.to_s]
60
+ case column
61
+ when String
62
+ @data[column.to_sym]
63
+ else
64
+ @data[column.to_s]
65
+ end
61
66
  end
62
67
  Vector.new(column_data)
63
68
  end
64
69
  end
65
70
 
71
+ def []=(key, values)
72
+ case key
73
+ when Symbol
74
+ sym_key = key
75
+ str_key = key.to_s
76
+ else
77
+ str_key = key.to_str
78
+ sym_key = str_key.to_sym
79
+ end
80
+ case
81
+ when @data.has_vector?(sym_key)
82
+ key = sym_key
83
+ when @data.has_vector?(str_key)
84
+ key = str_key
85
+ end
86
+
87
+ case values
88
+ when Charty::Vector
89
+ case values.adapter
90
+ when Charty::VectorAdapters::DaruVectorAdapter
91
+ @data[key] = values.adapter.data
92
+ else
93
+ @data[key] = values.to_a
94
+ end
95
+ else
96
+ orig_values = values
97
+ values = Array.try_convert(values)
98
+ if values.nil?
99
+ raise ArgumentError, "`values` must be convertible to Array"
100
+ end
101
+ @data[key] = values
102
+ end
103
+ return values
104
+ end
105
+
66
106
  private def check_type(type, data, name)
67
107
  return data if data.is_a?(type)
68
108
  raise TypeError, "#{name} must be a #{type}"
@@ -20,6 +20,8 @@ module Charty
20
20
  end
21
21
  when Hash
22
22
  true
23
+ when ->(x) { defined?(CSV::Table) && x.is_a?(CSV::Table) }
24
+ true
23
25
  end
24
26
  end
25
27
 
@@ -73,12 +75,17 @@ module Charty
73
75
  arrays[i] << record[key]
74
76
  end
75
77
  end
78
+ when Vector
79
+ arrays = data
76
80
  when self.class.method(:array?)
77
81
  unsupported_data_format unless data.all?(&self.class.method(:array?))
78
82
  arrays = data.map(&:to_a).transpose
79
83
  else
80
84
  unsupported_data_format
81
85
  end
86
+ when ->(x) { defined?(CSV::Table) && x.is_a?(CSV::Table) }
87
+ columns ||= data.headers
88
+ arrays = data.headers.map {|x| data[x] }
82
89
  else
83
90
  unsupported_data_format
84
91
  end
@@ -116,20 +123,15 @@ module Charty
116
123
  index = union_indexes(*indexes)
117
124
 
118
125
  arrays = arrays.map do |array|
119
- case array
120
- when Charty::Vector
121
- array.data
122
- when Hash
123
- raise NotImplementedError
124
- when self.class.method(:array?)
125
- array
126
- else
127
- Array.try_convert(array)
128
- end
126
+ Vector.try_convert(array)
129
127
  end
130
128
 
131
129
  columns = generate_column_names(arrays.length, columns)
132
130
 
131
+ arrays.zip(columns) do |array, column|
132
+ array.name = column.to_sym if array.name.to_s != column
133
+ end
134
+
133
135
  return arrays, columns, index
134
136
  end
135
137
 
@@ -176,6 +178,11 @@ module Charty
176
178
  @data[column][row]
177
179
  else
178
180
  case column
181
+ when Array
182
+ slice_data = column.map { |cn|
183
+ [cn, self[nil, cn]]
184
+ }.to_h
185
+ return Charty::Table.new(slice_data, index: self.index)
179
186
  when Symbol
180
187
  sym_key = column
181
188
  str_key = column.to_s
@@ -193,6 +200,47 @@ module Charty
193
200
  end
194
201
  end
195
202
 
203
+ def []=(key, values)
204
+ case key
205
+ when Symbol
206
+ str_key = key.to_s
207
+ sym_key = key
208
+ else
209
+ str_key = key.to_str
210
+ sym_key = str_key.to_sym
211
+ end
212
+
213
+ orig_values = values
214
+ values = Vector.try_convert(values)
215
+ if values.nil?
216
+ raise ArgumentError,
217
+ "`values` must be convertible to Charty::Vector"
218
+ end
219
+
220
+ if values.length != self.length
221
+ raise ArgumentError,
222
+ "`values` length does not match the length of the table"
223
+ end
224
+
225
+ if @data.key?(sym_key)
226
+ @data[sym_key] = values
227
+ elsif @data.key?(str_key)
228
+ @data[str_key] = values
229
+ elsif key == sym_key
230
+ @data[sym_key] = values
231
+ new_column = sym_key
232
+ else
233
+ @data[str_key] = values
234
+ new_column = sym_key
235
+ end
236
+
237
+ if new_column
238
+ self.columns = Index.new([*self.columns, new_column])
239
+ end
240
+
241
+ values
242
+ end
243
+
196
244
  def each
197
245
  i, n = 0, shape[0]
198
246
  while i < n
@@ -42,6 +42,10 @@ module Charty
42
42
  end
43
43
  end
44
44
 
45
+ def column?(name)
46
+ data.__contains__(name)
47
+ end
48
+
45
49
  def index
46
50
  PandasIndex.new(data.index)
47
51
  end
@@ -76,8 +80,33 @@ module Charty
76
80
  if row
77
81
  @data[column][row]
78
82
  else
79
- Vector.new(@data[column])
83
+ case column
84
+ when Array
85
+ Table.new(@data[column])
86
+ else
87
+ Vector.new(@data[column])
88
+ end
89
+ end
90
+ end
91
+
92
+ def []=(key, values)
93
+ case values
94
+ when Charty::Vector
95
+ case values.adapter
96
+ when Charty::VectorAdapters::PandasSeriesAdapter
97
+ @data[key] = values.adapter.data
98
+ else
99
+ @data[key] = values.to_a
100
+ end
101
+ else
102
+ orig_values = values
103
+ values = Array.try_convert(values)
104
+ if values.nil?
105
+ raise ArgumentError, "`values` must be convertible to Array"
106
+ end
107
+ @data[key] = values
80
108
  end
109
+ return values
81
110
  end
82
111
 
83
112
  def drop_na
@@ -101,6 +130,15 @@ module Charty
101
130
  Charty::Table.new(data.reset_index)
102
131
  end
103
132
 
133
+ def melt(id_vars: nil, value_vars: nil, var_name: nil, value_name: :value)
134
+ id_vars = check_melt_vars(id_vars, :id_vars) { nil }
135
+ value_vars = check_melt_vars(value_vars, :value_vars) { nil }
136
+
137
+ Charty::Table.new(data.melt(id_vars: id_vars, value_vars: value_vars,
138
+ var_name: var_name, value_name: value_name,
139
+ ignore_index: true))
140
+ end
141
+
104
142
  class GroupBy < Charty::Table::GroupByBase
105
143
  def initialize(groupby)
106
144
  @groupby = groupby
@@ -116,6 +154,7 @@ module Charty
116
154
  each_group_key.to_a
117
155
  end
118
156
 
157
+ # TODO: test
119
158
  def each_group_key
120
159
  return enum_for(__method__) unless block_given?
121
160
 
@@ -146,6 +185,15 @@ module Charty
146
185
  end
147
186
  end
148
187
 
188
+ # TODO: test
189
+ def each_group
190
+ return enum_for(__method__) unless block_given?
191
+
192
+ each_group_key do |key|
193
+ yield(Array(key), self[key])
194
+ end
195
+ end
196
+
149
197
  def apply(*args, &block)
150
198
  res = @groupby.apply(->(data) {
151
199
  res = block.call(Charty::Table.new(data), *args)